iOS现有项目集成React Native

简介

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 "[email protected]" has unmet peer dependency "[email protected]".

$ 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) => (
      
        {score.name}:{score.value}
        {'\n'}
      
    ));
    return (
      
        2048 High Scores!
        {contents}
      
    );
  }
}

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,这种编程方式,是使用了JSX语法,内容写在<>content,布局通过style来实现,最终给到组件.通过const styles = StyleSheet.create({});来创建布局.

客户端的入口文件RCTRootView

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

#import 
 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)