React Native集成到现有的原生项目
因为项目需求,在iOS原生项目中会嵌套几个RN界面,这就牵涉到了原生代码中嵌套RN代码的问题,至于集成步骤以及过程中遇到的坑都在下面一一列举,以帮助后来人。
前提
RN环境已经搭建完成,如若未完成,请移步
React Native环境搭建
将React Native集成到iOS应用中主要有如下几个步骤:
- 配置好React Native依赖和项目结构
- 了解你要集成的React Native组件
- 使用CocoaPods把这些组件以依赖的形式加入到项目中
- 创建js文件,编写React Native组件的js代码
- 在应用中添加一个
RCTRootView
,这个RCTRootView
正是用来承载你的React Native组件的容器- 启动React Native的Packager服务,运行应用
- 验证这部分组件是否正常工作
1. 配置项目目录结构
我在此也是为了更加深入的了解RN,所以在这里拿以前的小demo来做示范了。
首先创建一个空目录用于存放React Native项目,然后在这个空目录中创建一个ios
子目录,把现有的iOS项目拷贝到ios
子目录中
2. 安装JavaScript依赖包
在项目根目录下创建一个名为package.json
的文件,其中填入以下内容:
{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
}
}
接下来使用yarn
或者npm
(两者都是node的包管理器)来安装React和React Native模块。
打开终端,进入到根目录中(即package.json)文件所在目录,然后运行以下命令安装:
yarn add react-native
这样默认安装最新版本的React Native,命令执行完毕后可以看到输出中有两个警告信息:
warning " > react-native@0.57.4" has unmet peer dependency "react@16.6.0-alpha.8af6728".
warning Your current version of Yarn is out of date. The latest version is "1.12.3", while you're on "1.9.4".
第一个警告提示还要安装指定版本的React,第二个警告说是yarn版本不是最新的
然后执行命令安装指定版本的React
yarn add react@16.6.0-alpha.8af6728
注意:必须严格匹配警告信息中所列出的版本,高了或者低了都是不可以嘀!
完成这个步骤之后可以发现我们的根目录下多了一些东西:
根目录项目根目录下的node_modules
目录中安装的都是JavaScript的以来模块(对于这个目录,我们的原则是不复制、不移动、不修改、不上传、随用随装)
把node_modules/
目录记录到.gitignore
文件中(即不上传到版本控制系统,只保留到本地)
3. 安装CocoaPods
CocoaPods是针对iOS和Mac开发的包管理工具。执行一下命令安装CocoaPods
brew install cocoapods
安装就不用过多的说了(毕竟都安装的有)
4. 配置CocoaPods依赖
React Native框架整体是作为node模块安装到项目中的。接下来我们就要在CocoaPods的Podfile中指定我们需要使用的subspecs
。
可用的subspecs
都列在node_modules/react-native/React.podspec
文件中,基本上都是按照其功能命名的。一般来说第一个要添加的就是Core
,其包含了必须的AppRegistry
、StyleSheet
、View
以及其他的一些React Native核心库。如果要使用React Native的Text
库(即<Text>
组件),那就需要添加TCTText
的subspec
,等等。
我们需要在Podfile
文件中指定所需要的subspec
。创建Podfile
的最简单方法就是在/ios
目录中使用CocoaPods的init
命令(如果原先项目中已经创建了Podfile
文件,可以跳过此步):
pod init
这样Podfile
就已经创建了,接下来就需要调整其内容以满足集成需求,调整后的Podfile
内容类似下面的:
# target的名字一般与你的项目名字相同
target 'NumberTileGame' do
# 'node_modules'目录一般位于根目录中
# 但是如果你的结构不同,那你就要根据实际路径修改下面的`:path`
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', # 如果RN版本 >= 0.47则加入此行
'DevSupport', # 如果RN版本 >= 0.43,则需要加入此行才能开启开发者菜单
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 调试功能需要此模块
'RCTAnimation', # FlatList和原生动画功能需要此模块
# 在这里继续添加你所需要的其他RN模块
]
# 如果你的RN版本 >= 0.42.0,则加入下面这行
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
# 如果RN版本 >= 0.45则加入下面三个第三方编译依赖
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'
end
修改好Podfile
文件后,就可以开始安装React Native的pod包了:
pod install
5. RN代码集成
依赖搞好之后,就可以愉快的搞代码咯,接下来我们在根目录中创建一个名为home.js
的文件(个人建议功能文件单独创建文件夹放到一块,不要都挤在根目录中),实现以下内容(这里的内容可以随便咯):
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, NativeModules, NativeAppEventEmitter} from 'react-native';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
var rn = NativeModules.HmRNViewController;
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>Hello World</Text>
<Text style={styles.instructions}>这里是RN界面</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
代码码好了,接下来就是要iOS原生应用能够调用的到的这个页面,我们需要在项目根目录下创建一个空的index.js
文件(入口文件建议放在根目录下)
index.js
文件是React Native应用在iOS上的入口文件,该入口文件也可以像iOS的main
入口文件一样简单,在其中实现以下内容即可完成:
import {AppRegistry} from 'react-native';
import home from './home.js'; // 引入当前目录下的home.js文件
// 注册组件
AppRegistry.registerComponent('homePage', () => home);
6. iOS代码集成
RN代码搞定,接下来就是在iOS原生项目中对接RN了,这也是重点:
假设当前的原生项目中A界面有一个按钮,点击之后要让跳转到RN界面,主要实现如下:
在iOS项目中新建一个ViewController
界面B,在B界面的viewDidLoad
方法中实现以下代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"B";
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"homePage" initialProperties:@{@"name" : @"Max", @"value" : @"123456"} launchOptions:nil];
// 这里如果引入的是一个页面,可以使用self.view = rootView;,也可以使用[self.view addSubview:rootView];,不过使用addSubview:方法必须要给rootView设置frame
//如果是一个组件(如按钮、轮播图等等),则可以直接使用[self.view addSubview:rootView];将组件添加到当前view,并设置frame值:rootView.frame = CGRectMake(0, 100, 100, 100);
self.view = rootView;
}
A界面的按钮点击事件中实现的功能就是普通的跳转到B界面,这里就略去了
7. 运行项目
运行项目,点击A界面的按钮,即可跳转到RN界面:
报错咯别慌别慌,要稳住,不就是一个红屏嘛 哈哈哈
出现这个红屏报错的原因在于:有没有发现运行项目少了点什么?一般情况下,RN项目运行的时候都会自动启动packager服务的,但是这里没有自动启动,至于怎么设置自动启动packager服务我暂时也不知道,但是可以手动启动的:
打开终端,到达项目根目录下(即package.json
所在目录),然后执行命令:
npm start
然后终端形如:
启动packager服务刚刚打开的终端就用于启动packager服务了,想要运行项目要么再打开一个终端至根目录下运行react-native run-ios
,要么进入ios
文件夹打开Xcode运行,运行结果如下:
在RN界面如果不想要原生的导航栏,可以在B界面中隐藏导航栏,总之,随便搞起来吧
这篇文章所介绍的主要是更改目录结构,环境配置等等,具体代码部分很有限,所以源代码就不贴出来了。在后面还会陆续的贴出来我在学习RN中遇到的问题我和经验分享。