簡介
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 "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時間.這樣做有什么影響呢?
- 找不到文件,對于項目其他成員,因為沒有該文件夾,所以不管是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) => (
<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,
},
});
分析:
-
export default class AAA extends React.Component
的exprot default
是指輸出當前的組件,且輸出名為AAA. -
render
是用于更新UI的方法,這里有Render方法官文 - 關于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];
分析:
- 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