因為項目需求,在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服務了,想要運行項目要么再打開一個終端至根目錄下運行react-native run-ios
,要么進入ios
文件夾打開Xcode運行,運行結果如下:
在RN界面如果不想要原生的導航欄,可以在B界面中隱藏導航欄,總之,隨便搞起來吧
這篇文章所介紹的主要是更改目錄結構,環境配置等等,具體代碼部分很有限,所以源代碼就不貼出來了。在后面還會陸續的貼出來我在學習RN中遇到的問題我和經驗分享。