iOS現有項目集成React Native

簡介

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組件,現有了解一下的幾個概念:

  1. 創建RN依賴和文件夾的目錄結構
  2. 通過CocoaPods引入需要的RN組件
  3. 在現有通過RN實現的地方添加RCTRootView.這個view可以理解為是RN的容器
  4. 開啟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時間.這樣做有什么影響呢?

  1. 找不到文件,對于項目其他成員,因為沒有該文件夾,所以不管是JS端還是native端都有報找不到文件的問題.解決辦法,在package.json文件目錄下執行yarn進行安裝即可
  2. 持續集成有問題,如果使用了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.jsindex.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,
  },
});

分析:

  1. export default class AAA extends React.Componentexprot default是指輸出當前的組件,且輸出名為AAA.
  2. render是用于更新UI的方法,這里有Render方法官文
  3. 關于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];

分析:

  1. jsCodeLocation是作為頁面的資源來使用的.上面有兩種賦值方式,第一張是本地調試時,通過起服務,RN的端口號為8081來獲取. 第二種是通過main.jsbundle來獲取
  2. moduleName就是我們和index.ios.js約定的組件名,還記得嗎?就是寫在AppRegistry.registerComponent('Aname', () => AAA);里面的.
  3. initialProperties為初始化屬性,這里傳自己想要的值即可.傳到JS后,可以通過this.props來獲取,后期怎么通過native來更新呢?(比如登錄是客戶端做的,在登錄狀態發生變化時,告知JS),這個之后再說.感興趣的童鞋留言.
  4. 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官文

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容