iOS现有项目集成React Native

作者: sea_biscute | 来源:发表于2018-05-30 19:30 被阅读28次

    简介

    React Native已经出了很长时间了,随着使用范围越来越广,加入的项目越来越多,过去被人诟病的:首次加载时间长,性能监控和崩溃监控不成熟以及分包下载不完善,都已经有了较成熟的方案.
    对于RN还跃跃欲试的APP可以下水了,那么如何在在已有的项目中集成React Native,下面分析步骤.
    React Native可以理解为结合了React(这是一个JS的框架)和native(可以指安卓和iOS).在编写JS端代码是,需要我们对React有一定的了解.这里先不细说,贴个官文React,感兴趣或者后期React知识储备不足时可以看看.

    基本结构搭建

    brew install node
    brew install watchman
    npm install -g react-native-cli
    

    在iOS项目中集成RN组件,现有了解一下的几个概念:

    1. 创建RN依赖和文件夹的目录结构
    2. 通过CocoaPods引入需要的RN组件
    3. 在现有通过RN实现的地方添加RCTRootView.这个view可以理解为是RN的容器
    4. 开启RN服务,运行APP进行调试

    准备

    创建package.json,搭建React Native环境

    在项目下创建一个RN文件夹,进入RN的文件夹下,新建package.json文件,文件的格式大致如下

    {
      "name": "MyReactNativeApp",
      "version": "0.0.1",
      "private": true,
      "scripts": {
        "start": "node node_modules/react-native/local-cli/cli.js start"
      }
    
    }
    

    package.json的目录下,在命令行运行如下指令,如果提示yarn未安装,请进行yarn安装,懒得看可以直接运行brew install yarn进行安装

    $ yarn add react-native

    如果输出如下提示,说明react-native还需要依赖react包,好的,再安装react

    warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".

    $ yarn add react@(这里版本写上面提示的)

    以上安装完成之后,在当前文件夹下,就会出现一个新的/node_modules文件夹,这个文件夹内存储了所有的JS依赖.

    对node_modules进行gitignore

    这条不是必要项,但是建议这样做.ignore引发的问题解决办法下面有说明.

    .gitignore内添加node_modules/,因为node_modules/文件夹下的内容特别多,如果每次git push的时候都提交,太大了,添加了gitignore可以节省push时间.这样做有什么影响呢?

    1. 找不到文件,对于项目其他成员,因为没有该文件夹,所以不管是JS端还是native端都有报找不到文件的问题.解决办法,在package.json文件目录下执行yarn进行安装即可
    2. 持续集成有问题,如果使用了fastlane或者jenkins,会打包不成功,在集成命令内加入yarn,在每次构建之前先进行node_modules的安装

    在项目内集成RN

    配置CocoaPods依赖

    RN对于各个组件,通过subspec的方式进行了拆分,可以按需引入.在引入RN之前,首先确定想要引入哪些RN框架,通过pod去制定对应的subspec.可以在/node_modules/react-native/React.podspec文件内查看已提供的subspec.这里按下不表,需要的时候直接加就行.
    下面给个官方的样表

    # Your 'node_modules' directory is probably in the root of your project,
      # but if not, adjust the `:path` accordingly
      pod 'React', :path => '../node_modules/react-native', :subspecs => [
        'Core',
        'CxxBridge', # Include this for RN >= 0.47
        'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
        'RCTText',
        'RCTNetwork',
        'RCTWebSocket', # Needed for debugging
        'RCTAnimation', # Needed for FlatList and animations running on native UI thread
        # Add any other subspecs you want to use in your project
      ]
      # Explicitly include Yoga if you are using RN >= 0.42.0
      pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
    
      # Third party deps podspec link
      pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
      pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
      pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
    

    执行pod install

    代码集成

    React Native的组件

    添加index.ios.js入口文件

    RN的JS端的入口文件为index.js,这个是安卓和iOS共同的入口文件,如果想做区分可以声明index.ios.jsindex.android.js.
    index.ios.js作为入口文件,一般用于注册输出JS文件的其他组件,也就是JS各个页面的入口,内容大致如下,可以注册多个文件用于多个页面显示.其中Aname和Bname就是和native约定的名字.

    import {
      AppRegistry
    } from 'react-native'
    import A from './AAA'
    import B from './BBB'
    
    AppRegistry.registerComponent('Aname', () => A)
    AppRegistry.registerComponent('Bname', () => B)
    
    
    编写RN代码

    为了避免index.ios.js文件太大,阅读性差,我们会把对应的组件代码分到各个.js文件中.如上我们创建了一个AAA.js,给个模板

    import React from 'react';
    import { StyleSheet, Text, View} from 'react-native';
    
    export default class AAA extends React.Component {
      render() {
        var contents = this.props['scores'].map((score) => (
          <Text key={score.name}>
            {score.name}:{score.value}
            {'\n'}
          </Text>
        ));
        return (
          <View style={styles.container}>
            <Text style={styles.highScoresTitle}>2048 High Scores!</Text>
            <Text style={styles.scores}>{contents}</Text>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#FFFFFF',
      },
      highScoresTitle: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
      scores: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
      },
    });
    
    

    分析:

    1. export default class AAA extends React.Componentexprot default是指输出当前的组件,且输出名为AAA.
    2. render是用于更新UI的方法,这里有Render方法官文
    3. 关于JSX,<View style={styles.container}></View>这种编程方式,是使用了JSX语法,内容写在<>content</>,布局通过style来实现,最终给到组件.通过const styles = StyleSheet.create({});来创建布局.

    客户端的入口文件RCTRootView

    通过上面一系列操作,我们的RN组件已经书写完成并通过index.ios.js输出.那么如何添加到我们现有的controlle上面呢?RN提供了一个类RCTRootView作为客户端的RN容器.

    #import <React/RCTRootView.h>
     NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
    //jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
        RCTRootView *rootView =
          [[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                      moduleName: @"Aname"
                               initialProperties:
                                 @{
                                   @"scores" : @[
                                     @{
                                       @"name" : @"Alex",
                                       @"value": @"42"
                                      },
                                     @{
                                       @"name" : @"Joel",
                                       @"value": @"10"
                                     }
                                   ]
                                 }
                                   launchOptions: nil];
    

    分析:

    1. jsCodeLocation是作为页面的资源来使用的.上面有两种赋值方式,第一张是本地调试时,通过起服务,RN的端口号为8081来获取. 第二种是通过main.jsbundle来获取
    2. moduleName就是我们和index.ios.js约定的组件名,还记得吗?就是写在AppRegistry.registerComponent('Aname', () => AAA);里面的.
    3. initialProperties为初始化属性,这里传自己想要的值即可.传到JS后,可以通过this.props来获取,后期怎么通过native来更新呢?(比如登录是客户端做的,在登录状态发生变化时,告知JS),这个之后再说.感兴趣的童鞋留言.
    4. RCTRootView的initWithURL起了一个新的JSC VM.用于保存数据和简化native的RN不同view之间的通讯.当然,你也可以多个组件关联一个JS runtime.如果想整牙做,就不要使用initWithURL,而是通过RCTBridge initWithBundleURL创建一个RCTBridge对象,在通过RCTRootView initWithBridge来生成RCTRootView.

    开始调试

    通过调试确认集成成功

    运行服务

    在index.ios.js文件夹下,运行如下命令起服务.
    $ npm start

    运行APP

    可以直接运行APP,也可以在项目的.xcodeproj所在文件夹下通过以下命令运行
    $ react-native run-ios

    资料

    RN官文

    相关文章

      网友评论

        本文标题:iOS现有项目集成React Native

        本文链接:https://www.haomeiwen.com/subject/djlxsftx.html