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 "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官文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容