React Native (11)引入原生模块& UI

本文章主要介绍如何在 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图片资源文件

image.png

2. RN桥接原生模块

定义一个ConnectTool 的类,提供给React Native调用,首先这个类需要遵守 协议;
RCT_EXPORT_MODULE(); 默认导出以该类名为名字的原生模块;

    1. 导出一个异步方法
RCT_EXPORT_METHOD(openView:(NSDictionary*)params){
  // 因为是显示页面,所以让原生接口运行在主线程
     NSLog(@"start openView:");
     dispatch_async(dispatch_get_main_queue(), ^{
          sleep(3.0);
         // 在这里可以写需要原生处理的UI或者逻辑
         NSLog(@"end openView = %@", params);
     });
}
    1. 导出一个支持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++;
  });
}

    1. 导出一个同步方法
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;
}

    1. 导出一个常量方法
-(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 调用结果


image.png

方法2 调用结果


IMG_4867 2.jpg

方法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中的调用

效果图如下(这里只是举个例子,样式写的比较垃圾):


原生view在RN中展示.jpg

3. RN动态更新

  1. React Native 中文网的 Pushy
  2. 微软的 CodePush
  3. 搭建私服的 code-push-server。

3. RN图片资源本地化

4. RN发起网络请求 Get、Post

参考链接 React-Native JS 加载原生组件(iOS)
参考链接 详解RN导出Native Module原理

你可能感兴趣的:(React Native (11)引入原生模块& UI)