简介
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组件,现有了解一下的几个概念:
- 创建RN依赖和文件夹的目录结构
- 通过CocoaPods引入需要的RN组件
- 在现有通过RN实现的地方添加
RCTRootView
.这个view可以理解为是RN的容器 - 开启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时间.这样做有什么影响呢?
- 找不到文件,对于项目其他成员,因为没有该文件夹,所以不管是JS端还是native端都有报找不到文件的问题.解决办法,在
package.json
文件目录下执行yarn
进行安装即可 - 持续集成有问题,如果使用了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.js
和index.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,
},
});
分析:
-
export default class AAA extends React.Component
的exprot default
是指输出当前的组件,且输出名为AAA. -
render
是用于更新UI的方法,这里有Render方法官文 - 关于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];
分析:
- jsCodeLocation是作为页面的资源来使用的.上面有两种赋值方式,第一张是本地调试时,通过起服务,RN的端口号为
8081
来获取. 第二种是通过main.jsbundle
来获取- moduleName就是我们和index.ios.js约定的组件名,还记得吗?就是写在
AppRegistry.registerComponent('Aname', () => AAA);
里面的.initialProperties
为初始化属性,这里传自己想要的值即可.传到JS后,可以通过this.props
来获取,后期怎么通过native来更新呢?(比如登录是客户端做的,在登录状态发生变化时,告知JS),这个之后再说.感兴趣的童鞋留言.- 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官文