React Native整合到現有的原生專案

weixin_33670713發表於2018-11-09

因為專案需求,在iOS原生專案中會巢狀幾個RN介面,這就牽涉到了原生程式碼中巢狀RN程式碼的問題,至於整合步驟以及過程中遇到的坑都在下面一一列舉,以幫助後來人。

前提

RN環境已經搭建完成,如若未完成,請移步
React Native環境搭建

將React Native整合到iOS應用中主要有如下幾個步驟:

  1. 配置好React Native依賴和專案結構
  2. 瞭解你要整合的React Native元件
  3. 使用CocoaPods把這些元件以依賴的形式加入到專案中
  4. 建立js檔案,編寫React Native元件的js程式碼
  5. 在應用中新增一個RCTRootView,這個RCTRootView正是用來承載你的React Native元件的容器
  6. 啟動React Native的Packager服務,執行應用
  7. 驗證這部分元件是否正常工作

1. 配置專案目錄結構

我在此也是為了更加深入的瞭解RN,所以在這裡拿以前的小demo來做示範了。
首先建立一個空目錄用於存放React Native專案,然後在這個空目錄中建立一個ios子目錄,把現有的iOS專案拷貝到ios子目錄中

1900083-87aa781e7e48f878.png
目錄結構

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

注意:必須嚴格匹配警告資訊中所列出的版本,高了或者低了都是不可以嘀!

完成這個步驟之後可以發現我們的根目錄下多了一些東西:

1900083-54fd1df335795b07.png
根目錄

專案根目錄下的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,其包含了必須的AppRegistryStyleSheetView以及其他的一些React Native核心庫。如果要使用React Native的Text庫(即<Text>元件),那就需要新增TCTTextsubspec,等等。

我們需要在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介面:

1900083-60c5371edc560724.png
報錯咯

別慌別慌,要穩住,不就是一個紅屏嘛 哈哈哈
出現這個紅屏報錯的原因在於:有沒有發現執行專案少了點什麼?一般情況下,RN專案執行的時候都會自動啟動packager服務的,但是這裡沒有自動啟動,至於怎麼設定自動啟動packager服務我暫時也不知道,但是可以手動啟動的:

開啟終端,到達專案根目錄下(即package.json所在目錄),然後執行命令:

npm start

然後終端形如:

1900083-d3ab45c16bc044e4.png
啟動packager服務

剛剛開啟的終端就用於啟動packager服務了,想要執行專案要麼再開啟一個終端至根目錄下執行react-native run-ios,要麼進入ios資料夾開啟Xcode執行,執行結果如下:

1900083-9636cc1a55c52a25.png
執行結果

在RN介面如果不想要原生的導航欄,可以在B介面中隱藏導航欄,總之,隨便搞起來吧

這篇文章所介紹的主要是更改目錄結構,環境配置等等,具體程式碼部分很有限,所以原始碼就不貼出來了。在後面還會陸續的貼出來我在學習RN中遇到的問題我和經驗分享。

相關文章