本文章主要介绍如何在 React Native
中使用原生模块、以及在ReactNative
中如何使用原生UI
作为 Component
;
项目中用到的源码 ReactNative_demo
1.打包JSbundle
进行编译,离线打包资源。命令如下:
react-native bundle
--entry-file index.js //entry-file,ios或者android入口的js名称,比如index.js
--platform ios //platform ,平台名称(ios或者android)
--dev false //设置为false时会对JavaScript代码进行优化处理
--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为脚本,固定写就行)
提前在项目根目录的 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"
},
执行命令
yarn bundle-ios
最后输出jsbundle
和 图片资源文件
2. RN桥接原生模块
定义一个ConnectTool
的类,提供给React Native
调用,首先这个类需要遵守
协议;
RCT_EXPORT_MODULE();
默认导出以该类名为名字的原生模块;
-
- 导出一个异步方法
RCT_EXPORT_METHOD(openView:(NSDictionary*)params){
// 因为是显示页面,所以让原生接口运行在主线程
NSLog(@"start openView:");
dispatch_async(dispatch_get_main_queue(), ^{
sleep(3.0);
// 在这里可以写需要原生处理的UI或者逻辑
NSLog(@"end openView = %@", params);
});
}
-
- 导出一个支持
Promise
的方法
- 导出一个支持
RCT_EXPORT_METHOD(request2:(NSDictionary *)params success:(RCTPromiseResolveBlock)success failed:(RCTPromiseRejectBlock)failed){
NSMutableDictionary *paramsMutable = @{@"result":@"success"}.mutableCopy;
/// 这里模拟一个网络请求 成功/失败的情况
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); //模拟网络请求
if ((weakSelf.value % 2 == 0) && success != NULL) {
success(paramsMutable);
} else {
NSError *error = [NSError errorWithDomain:@"我是Promise回调错误信息..." code:101 userInfo:nil];
failed( @"-1",@"failed ",error);
}
weakSelf.value++;
});
}
-
- 导出一个同步方法
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;
}
-
- 导出一个常量方法
-(NSDictionary *)constDict{
return @{@"key1":@"value1",
@"key2":@"value2",
@"key3":@"value3",
};
}
在 React Native 的调用方法
提供一个文件封装原生模块 ,文件名文NativeIOSModule
;
/// 把这个原生模块封装起来exprot 导出(文件名为NativeIOSModule)
import {NativeModules} from 'react-native';
export default NativeModules.ConnectTools;
调用地方
import ConnectTools from '../Native/NativeIOSModule';
省略...
render(){
return(
this.onPress()}>
{this.props.titleName}
)
}
async onPress(state) {
let value = {'title':'pengchao'};
//func1 (异步方法 无回调)
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 调用结果
方法2 调用结果
方法3 调用结果
略...
方法4 调用结果
略...
2. RN桥接原生UI
桥接原生的UI ,需要实现两个对象,一个是自定义的View
和继承于RCTViewManager
的子类 ;
#import
#import
#import
#import "CustomButton.h"
/// 自定义VIew
@interface CustomButton : UIView
@property (nonatomic,copy) NSString *mapData;//RN 组件传来的属性
@property (nonatomic,copy) RCTBubblingEventBlock onButtonClick; //回调方法
@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];
}
/// 响应点击事件
- (void)clickFunc:(UIButton *)sender {
NSLog(@"clickFunc");
self.value ++;
// @{@"key":@(self.value) 这里表示需要回传给外部的值
self.onButtonClick(@{@"key":@(self.value)});
}
/// RN 部分的属性被赋值后会自动调用这个方法传参
-(void)setTitleName:(NSString *)titleName{
if (titleName) {
[self.leftButton setTitle:titleName forState:UIControlStateNormal];
}
[self layoutSubviews];
}
/// 自定义一些其它参数的逻辑
- (void)setMapData:(NSString *)mapData {
NSLog(@"%@",mapData);
}
@end
@implementation CustomButtonView
RCT_EXPORT_MODULE();
// props 参数 (定义的参数要在相应的view实现方法)
RCT_EXPORT_VIEW_PROPERTY(titleName, NSString)
// 字典类型 ,参数名 mapData
RCT_EXPORT_VIEW_PROPERTY(mapData, NSDictionary)
// 点击事件
RCT_EXPORT_VIEW_PROPERTY(onButtonClick, RCTBubblingEventBlock)
// 自定义数据结构转json
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, CustomButtonView){
}
- (UIView *)view
{
/// frame不设置也行,反正都会被覆盖
if (!_customBtn) {
_customBtn = [[CustomButton alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
}
return _customBtn;
}
注意事项:
RCT_EXPORT_VIEW_PROPERTY()
自定义的属性,需要在自定义View内部实现,不然会出现找不到该方法的报错导致崩溃
//导出原生view 模块
import React, { Component } from 'react';
import { requireNativeComponent } from 'react-native';
var CustomButtonView = requireNativeComponent('CustomButtonView');
export default class CustomNativeButton extends Component {
render() {
return (
);
}
}
调用
/// 导入
import CustomNativeButton from './UI/CustomNativeButton'
/// 调用
{
console.log("xxxx" + result.key);
}}
mapData = {{mapKey:"mapValue"}}
style={{width: 300, height:100,flex:1}} >
在React Native
中的调用
效果图如下(这里只是举个例子,样式写的比较垃圾):
3. RN动态更新
- React Native 中文网的 Pushy
- 微软的 CodePush
- 搭建私服的 code-push-server。
3. RN图片资源本地化
4. RN发起网络请求 Get、Post
参考链接 React-Native JS 加载原生组件(iOS)
参考链接 详解RN导出Native Module原理