簡介
React Native已經(jīng)出了很長時(shí)間了,隨著使用范圍越來越廣,加入的項(xiàng)目越來越多,過去被人詬病的:首次加載時(shí)間長,性能監(jiān)控和崩潰監(jiān)控不成熟以及分包下載不完善,都已經(jīng)有了較成熟的方案.
對于RN還躍躍欲試的APP可以下水了,那么如何在在已有的項(xiàng)目中集成React Native,下面分析步驟.
React Native可以理解為結(jié)合了React(這是一個(gè)JS的框架)和native(可以指安卓和iOS).在編寫JS端代碼是,需要我們對React有一定的了解.這里先不細(xì)說,貼個(gè)官文React,感興趣或者后期React知識(shí)儲(chǔ)備不足時(shí)可以看看.
基本結(jié)構(gòu)搭建
brew install node
brew install watchman
npm install -g react-native-cli
在iOS項(xiàng)目中集成RN組件,現(xiàn)有了解一下的幾個(gè)概念:
- 創(chuàng)建RN依賴和文件夾的目錄結(jié)構(gòu)
- 通過CocoaPods引入需要的RN組件
- 在現(xiàn)有通過RN實(shí)現(xiàn)的地方添加
RCTRootView.這個(gè)view可以理解為是RN的容器 - 開啟RN服務(wù),運(yùn)行APP進(jìn)行調(diào)試
準(zhǔn)備
創(chuàng)建package.json,搭建React Native環(huán)境
在項(xiàng)目下創(chuàng)建一個(gè)RN文件夾,進(jìn)入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的目錄下,在命令行運(yùn)行如下指令,如果提示yarn未安裝,請進(jìn)行yarn安裝,懶得看可以直接運(yùn)行brew install yarn進(jìn)行安裝
$ 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@(這里版本寫上面提示的)
以上安裝完成之后,在當(dāng)前文件夾下,就會(huì)出現(xiàn)一個(gè)新的/node_modules文件夾,這個(gè)文件夾內(nèi)存儲(chǔ)了所有的JS依賴.
對node_modules進(jìn)行g(shù)itignore
這條不是必要項(xiàng),但是建議這樣做.ignore引發(fā)的問題解決辦法下面有說明.
在.gitignore內(nèi)添加node_modules/,因?yàn)?code>node_modules/文件夾下的內(nèi)容特別多,如果每次git push的時(shí)候都提交,太大了,添加了gitignore可以節(jié)省push時(shí)間.這樣做有什么影響呢?
- 找不到文件,對于項(xiàng)目其他成員,因?yàn)闆]有該文件夾,所以不管是JS端還是native端都有報(bào)找不到文件的問題.解決辦法,在
package.json文件目錄下執(zhí)行yarn進(jìn)行安裝即可 - 持續(xù)集成有問題,如果使用了fastlane或者jenkins,會(huì)打包不成功,在集成命令內(nèi)加入
yarn,在每次構(gòu)建之前先進(jìn)行node_modules的安裝
在項(xiàng)目內(nèi)集成RN
配置CocoaPods依賴
RN對于各個(gè)組件,通過subspec的方式進(jìn)行了拆分,可以按需引入.在引入RN之前,首先確定想要引入哪些RN框架,通過pod去制定對應(yīng)的subspec.可以在/node_modules/react-native/React.podspec文件內(nèi)查看已提供的subspec.這里按下不表,需要的時(shí)候直接加就行.
下面給個(gè)官方的樣表
# 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'
執(zhí)行pod install
代碼集成
React Native的組件
添加index.ios.js入口文件
RN的JS端的入口文件為index.js,這個(gè)是安卓和iOS共同的入口文件,如果想做區(qū)分可以聲明index.ios.js和index.android.js.
index.ios.js作為入口文件,一般用于注冊輸出JS文件的其他組件,也就是JS各個(gè)頁面的入口,內(nèi)容大致如下,可以注冊多個(gè)文件用于多個(gè)頁面顯示.其中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文件太大,閱讀性差,我們會(huì)把對應(yīng)的組件代碼分到各個(gè).js文件中.如上我們創(chuàng)建了一個(gè)AAA.js,給個(gè)模板
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是指輸出當(dāng)前的組件,且輸出名為AAA. -
render是用于更新UI的方法,這里有Render方法官文 - 關(guān)于JSX,
<View style={styles.container}></View>這種編程方式,是使用了JSX語法,內(nèi)容寫在<>content</>,布局通過style來實(shí)現(xiàn),最終給到組件.通過const styles = StyleSheet.create({});來創(chuàng)建布局.
客戶端的入口文件RCTRootView
通過上面一系列操作,我們的RN組件已經(jīng)書寫完成并通過index.ios.js輸出.那么如何添加到我們現(xiàn)有的controlle上面呢?RN提供了一個(gè)類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是作為頁面的資源來使用的.上面有兩種賦值方式,第一張是本地調(diào)試時(shí),通過起服務(wù),RN的端口號(hào)為
8081來獲取. 第二種是通過main.jsbundle來獲取- moduleName就是我們和index.ios.js約定的組件名,還記得嗎?就是寫在
AppRegistry.registerComponent('Aname', () => AAA);里面的.initialProperties為初始化屬性,這里傳自己想要的值即可.傳到JS后,可以通過this.props來獲取,后期怎么通過native來更新呢?(比如登錄是客戶端做的,在登錄狀態(tài)發(fā)生變化時(shí),告知JS),這個(gè)之后再說.感興趣的童鞋留言.- RCTRootView的initWithURL起了一個(gè)新的JSC VM.用于保存數(shù)據(jù)和簡化native的RN不同view之間的通訊.當(dāng)然,你也可以多個(gè)組件關(guān)聯(lián)一個(gè)JS runtime.如果想整牙做,就不要使用initWithURL,而是通過
RCTBridge initWithBundleURL創(chuàng)建一個(gè)RCTBridge對象,在通過RCTRootView initWithBridge來生成RCTRootView.
開始調(diào)試
通過調(diào)試確認(rèn)集成成功
運(yùn)行服務(wù)
在index.ios.js文件夾下,運(yùn)行如下命令起服務(wù).
$ npm start
運(yùn)行APP
可以直接運(yùn)行APP,也可以在項(xiàng)目的.xcodeproj所在文件夾下通過以下命令運(yùn)行
$ react-native run-ios