iOS現(xiàn)有項(xiàng)目集成React Native

簡介

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è)概念:

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

  1. 找不到文件,對于項(xiàng)目其他成員,因?yàn)闆]有該文件夾,所以不管是JS端還是native端都有報(bào)找不到文件的問題.解決辦法,在package.json文件目錄下執(zhí)行yarn進(jìn)行安裝即可
  2. 持續(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.jsindex.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,
  },
});

分析:

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

分析:

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

資料

RN官文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容