本文章主要介紹如何在 React Native中使用原生模塊、以及在ReactNative 中如何使用原生UI 作為 Component;
項(xiàng)目中用到的源碼 ReactNative_demo
1.打包JSbundle
進(jìn)行編譯,離線打包資源。命令如下:
react-native bundle
--entry-file index.js //entry-file,ios或者android入口的js名稱,比如index.js
--platform ios //platform ,平臺(tái)名稱(ios或者android)
--dev false //設(shè)置為false時(shí)會(huì)對(duì)JavaScript代碼進(jìn)行優(yōu)化處理
--bundle-output ./ios/bundle/index.ios.jsbundle //生成的jsbundle文件的名稱
--assets-dest ./ios/bundle //圖片以及其他資源存放的目錄,比如./ios/bundle
為了方便操作,在package.json中添加編譯命令(node node_modules/react-native/local-cli/cli.js為腳本,固定寫就行)
提前在項(xiàng)目根目錄的 iOS 目錄下新建好 bundle文件夾
"scripts": {
此處省略其它配置....
"bundle-ios" : "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.ios.jsbundle --assets-dest ./output/bundle"
},
執(zhí)行命令
yarn bundle-ios
最后輸出jsbundle 和 圖片資源文件

2. RN橋接原生模塊
定義一個(gè)ConnectTool 的類,提供給React Native調(diào)用,首先這個(gè)類需要遵守 <RCTBridgeModule>協(xié)議;
RCT_EXPORT_MODULE(); 默認(rèn)導(dǎo)出以該類名為名字的原生模塊;
- 導(dǎo)出一個(gè)異步方法
RCT_EXPORT_METHOD(openView:(NSDictionary*)params){
// 因?yàn)槭秋@示頁(yè)面,所以讓原生接口運(yùn)行在主線程
NSLog(@"start openView:");
dispatch_async(dispatch_get_main_queue(), ^{
sleep(3.0);
// 在這里可以寫需要原生處理的UI或者邏輯
NSLog(@"end openView = %@", params);
});
}
- 導(dǎo)出一個(gè)支持
Promise的方法
- 導(dǎo)出一個(gè)支持
RCT_EXPORT_METHOD(request2:(NSDictionary *)params success:(RCTPromiseResolveBlock)success failed:(RCTPromiseRejectBlock)failed){
NSMutableDictionary *paramsMutable = @{@"result":@"success"}.mutableCopy;
/// 這里模擬一個(gè)網(wǎng)絡(luò)請(qǐng)求 成功/失敗的情況
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) weakSelf = self;
dispatch_async(queue, ^{
[paramsMutable setValue:@(1) forKey:@"success"];
sleep(1.0); //模擬網(wǎng)絡(luò)請(qǐng)求
if ((weakSelf.value % 2 == 0) && success != NULL) {
success(paramsMutable);
} else {
NSError *error = [NSError errorWithDomain:@"我是Promise回調(diào)錯(cuò)誤信息..." code:101 userInfo:nil];
failed( @"-1",@"failed ",error);
}
weakSelf.value++;
});
}
- 導(dǎo)出一個(gè)同步方法
RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSArray *,testSyncFunc:(NSString *)name)
{
NSMutableArray *events = @[@"value1",@"value2",@"value3",@"value4",@"value5"].mutableCopy;
[events insertObject:name atIndex:0];
return events.copy;
}
- 導(dǎo)出一個(gè)常量方法
-(NSDictionary *)constDict{
return @{@"key1":@"value1",
@"key2":@"value2",
@"key3":@"value3",
};
}
在 React Native 的調(diào)用方法
提供一個(gè)文件封裝原生模塊 ,文件名文NativeIOSModule;
/// 把這個(gè)原生模塊封裝起來(lái)exprot 導(dǎo)出(文件名為NativeIOSModule)
import {NativeModules} from 'react-native';
export default NativeModules.ConnectTools;
調(diào)用地方
import ConnectTools from '../Native/NativeIOSModule';
省略...
render(){
return(
<View>
<TouchableHighlight onPress={()=>this.onPress()}>
<Text style={{color: 'red',fontSize:34,fontWeight:'bold'}}>{this.props.titleName}</Text>
</TouchableHighlight>
</View>
)
}
async onPress(state) {
let value = {'title':'pengchao'};
//func1 (異步方法 無(wú)回調(diào))
ConnectTools.openView(value);
// func2 (Promise 方法)
ConnectTools.request2(value).then((result)=>{
console.log('success'+ JSON.stringify(result));
},(code,message,error)=>{
console.log(code + message + error);
//coder \ message\ error ,只收到了 code == 'failed'
});
///func3 同步方法
let value2 = ConnectTools.testSyncFunc('value4');
alert(JSON.stringify(value2));
//func4
ConnectTools.request("deviceName", function(error,result1,result2){
console.log(error);
console.log(result1);
console.log(result2);
});
}
方法1 調(diào)用結(jié)果

方法2 調(diào)用結(jié)果

方法3 調(diào)用結(jié)果
略...
方法4 調(diào)用結(jié)果
略...
2. RN橋接原生UI
橋接原生的UI ,需要實(shí)現(xiàn)兩個(gè)對(duì)象,一個(gè)是自定義的View 和繼承于RCTViewManager的子類 ;
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <React/RCTViewManager.h>
#import "CustomButton.h"
/// 自定義VIew
@interface CustomButton : UIView
@property (nonatomic,copy) NSString *mapData;//RN 組件傳來(lái)的屬性
@property (nonatomic,copy) RCTBubblingEventBlock onButtonClick; //回調(diào)方法
@end
/// Bridger
@interface CustomButtonView : RCTViewManager
@property (nonatomic) CustomButton *customBtn;
@end
#import "CustomButton.h"
@interface CustomButton()
@property (nonatomic,strong) UIButton *leftButton;
@property (nonatomic,strong) UIImageView *rightImage;
@property (nonatomic,assign) NSUInteger value;
@end
@implementation CustomButton
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupUI];
self.value = 0;
}
return self;
}
- (void)setupUI {
/// 自定義UI的布局 & 初始化
self.leftButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
[self.leftButton setTitle:@"custom" forState:UIControlStateNormal];
[self.leftButton.titleLabel setFont:[UIFont systemFontOfSize:14]];
[self.leftButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
self.rightImage = [[UIImageView alloc]initWithFrame:CGRectMake(60, 0, 40, 40)];
self.rightImage.image =[UIImage imageNamed:@"test_icon"];
/// 事件
[self.leftButton addTarget:self action:@selector(clickFunc:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.leftButton];
[self addSubview:self.rightImage];
}
/// 響應(yīng)點(diǎn)擊事件
- (void)clickFunc:(UIButton *)sender {
NSLog(@"clickFunc");
self.value ++;
// @{@"key":@(self.value) 這里表示需要回傳給外部的值
self.onButtonClick(@{@"key":@(self.value)});
}
/// RN 部分的屬性被賦值后會(huì)自動(dòng)調(diào)用這個(gè)方法傳參
-(void)setTitleName:(NSString *)titleName{
if (titleName) {
[self.leftButton setTitle:titleName forState:UIControlStateNormal];
}
[self layoutSubviews];
}
/// 自定義一些其它參數(shù)的邏輯
- (void)setMapData:(NSString *)mapData {
NSLog(@"%@",mapData);
}
@end
@implementation CustomButtonView
RCT_EXPORT_MODULE();
// props 參數(shù) (定義的參數(shù)要在相應(yīng)的view實(shí)現(xiàn)方法)
RCT_EXPORT_VIEW_PROPERTY(titleName, NSString)
// 字典類型 ,參數(shù)名 mapData
RCT_EXPORT_VIEW_PROPERTY(mapData, NSDictionary)
// 點(diǎn)擊事件
RCT_EXPORT_VIEW_PROPERTY(onButtonClick, RCTBubblingEventBlock)
// 自定義數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)json
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, CustomButtonView){
}
- (UIView *)view
{
/// frame不設(shè)置也行,反正都會(huì)被覆蓋
if (!_customBtn) {
_customBtn = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
}
return _customBtn;
}
注意事項(xiàng):
RCT_EXPORT_VIEW_PROPERTY()自定義的屬性,需要在自定義View內(nèi)部實(shí)現(xiàn),不然會(huì)出現(xiàn)找不到該方法的報(bào)錯(cuò)導(dǎo)致崩潰
//導(dǎo)出原生view 模塊
import React, { Component } from 'react';
import { requireNativeComponent } from 'react-native';
var CustomButtonView = requireNativeComponent('CustomButtonView');
export default class CustomNativeButton extends Component {
render() {
return (
<CustomButtonView {...this.props}></CustomButtonView>
);
}
}
調(diào)用
/// 導(dǎo)入
import CustomNativeButton from './UI/CustomNativeButton'
/// 調(diào)用
<CustomNativeButton
titleName = 'CustomNativeButton'
onButtonClick={(result)=>{
console.log("xxxx" + result.key);
}}
mapData = {{mapKey:"mapValue"}}
style={{width: 300, height:100,flex:1}} >
</CustomNativeButton>
在React Native中的調(diào)用
效果圖如下(這里只是舉個(gè)例子,樣式寫的比較垃圾):

3. RN動(dòng)態(tài)更新
- React Native 中文網(wǎng)的 Pushy
- 微軟的 CodePush
- 搭建私服的 code-push-server。
3. RN圖片資源本地化
4. RN發(fā)起網(wǎng)絡(luò)請(qǐng)求 Get、Post
參考鏈接 React-Native JS 加載原生組件(iOS)
參考鏈接 詳解RN導(dǎo)出Native Module原理