React Native 整合指南

React-Native

React-Native 目前是一個不錯的多平臺開發和快速開發的解決方案,它解決了不少開發上的痛點,而且做爲 facebook 大廠出品,維護上有保障,所以學習 RN 是有好處的。固然,它目前還存在着不少的問題,可是均可以經過一些方案來規避和解決。RN 目前來講對 iOS 開發者比較友好,而 Android 開發環境則比較難以配置。node

HelloWorld

學什麼都得先來個 HelloWorld,RN 官方文檔對 HelloWorld 已經講的很詳細了。react

> brew install node
> brew install watchman
> npm install -g react-native-cli
> react-native init HelloWorld

而後就自動建立了對應的 iOS 和 Android 工程,筆者本身使用的是 node4-lts 版本,watchman 用於監視文件是否改變,不過筆者記得之前版本 watchman 是經過 npm 安裝的,而且不是強制要求。可能如今版本改動了,不裝就不能運行。android

iOS
{
  "name": "HelloWorld",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "15.3.2",
    "react-native": "0.34.1"
  }
}

這是 package.json 文件內容,裏面依賴了 react 和 react-native。打開 iOS 工程,裏面基本和普通的 iOS 工程差很少,Libraries 目錄包括了 node_modules 文件夾下的依賴工程,React.xcodeproj 工程內含兩段腳本,一個是 RCTJSCProfiler 分析,一個是啓動本地 node 服務器。AppDelegate.m 以下ios

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"HelloWorld"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

主要的區別就是使用 RCTRootView 做爲 RN 渲染視圖添加到控制器上。而且指定了 bundleURL 和 moduleName,看過源碼的應該知道,bundleURL 就是本地或者遠程 jsbundle 路徑,而 moduleName 就是 js 代碼中註冊的模塊名,jsBundleURLForBundleRoot:fallbackResource 則是返回本地或者遠程的 jsbundle 路徑,這裏就不細講了,只要點開源碼一看就懂。npm

Android

Android 環境配置比 iOS 麻煩多了,由於 iOS URL 等配置都是明文放在 AppDelegate 代碼中的,哪裏建立了 RootView 都很清楚,而安卓就比較麻煩,MainApplication 實現了 ReactApplication 接口,主要是生成了 mReactNativeHost 成員變量。mReactNativeHost 持有 ReactInstanceManager 類變量,初始化代碼以下json

ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
      .setApplication(mApplication)
      .setJSMainModuleName(getJSMainModuleName())
      .setUseDeveloperSupport(getUseDeveloperSupport())
      .setRedBoxHandler(getRedBoxHandler())
      .setUIImplementationProvider(getUIImplementationProvider())
      .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

這裏能夠看到指定了 jsMainModuleName 和是否顯示開發者菜單的參數,再來看 MainActivity,它繼承了 ReactActivity,只重寫了一個方法react-native

/**
 * Returns the name of the main component registered from JavaScript.
 * This is used to schedule rendering of the component.
 */
@Override
protected String getMainComponentName() {
    return "HelloWorld";
}

再來看它的父類 ReactActivity,裏面有一個成員變量 ReactActivityDelegate,它接受兩個參數初始化xcode

new ReactActivityDelegate(this, getMainComponentName());

這就變相的將組件名傳入了 mDelegate。再進入 ReactActivityDelegate,就能找到服務器

private @Nullable ReactRootView mReactRootView;

這就是 RootView,而後它的初始化以下app

protected void loadApp(String appKey) {
    if (mReactRootView != null) {
        throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    getPlainActivity().setContentView(mReactRootView);
}

這裏的初始化實際上就是對應了 iOS 中 rootView 的初始化,只不過 Android 是 startReactApplication 而不是經過構造函數傳遞參數的方式。而且,主要的參數都是經過 ReactInstanceManager 來存儲管理,appKey 實際上就是組件名,等同於 iOS 中的 moduleName,而 ReactInstanceManager 初始化中的 setJSMainModuleName 纔是 jsbundle 名稱。Android 甚至沒有在代碼中指定 remote server jsbundle 路徑的地方,目前只能進入 app 而後打開 dev setting 來修改遠程服務器地址,固然,也多是筆者沒有找到,若是有找到的朋友請在評論中告訴我。並且 ReactInstanceManager 初始化後面還有一小段代碼

String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
    builder.setJSBundleFile(jsBundleFile);
} else {
    builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}

發現了沒,iOS 下遠程 jsbundle 和本地 jsbundle 是經過一個方法統一處理的,而 Android 則須要兩個參數維護。iOS 下 RN 只提供了 RootView,開發者只須要處理好 RootView 暴露的方法就好了,更加簡單,而 Android 環境下則是提供了 ReactActivity 類來作封裝,而且將參數拆成了好幾個類,很是的麻煩。

Integration With Existing Apps

iOS

手動 iOS 整合步驟以下:

  1. package.json

  2. 建立帶有 subspecs 的 Podfile 用於引入所須要的第三方庫,這須要 CocoaPods,雖然也能夠手動 link 第三方庫,可是目前筆者推薦使用 CocoaPods

  3. index.ios.js

  4. RCTRootView

固然,還有其餘步驟,好比 ATS 的問題,不過一個合格的開發者應當都能完成,看上面這些步驟,你會發現實際上在 iOS 下,手動整合和 HelloWorld 基本是一致的。

Android

手動整合步驟以下

  1. package.json

  2. 增長 com.facebook.react:react-native:+maven {} 到 build.gradle

  3. index.android.js

  4. ReactRootView

是否發現了,在 Android 環境下,HelloWorld 工程和手動整合指南是不同的,最主要的區別在於 HelloWorld 工程是繼承 ReactActivity 來產生 Activity 的,而手動整合則是能夠隨便作,甚至能夠在 Fragment 上面建立 RootView。

總結

就目前而言,RN 在 iOS 下不管是表現仍是編碼難度,都優於 Android 環境,Android 官方甚至沒有一個很好的啓動 App 方式,只能 react-native run-android。並且在性能方面,iOS 通過良好的編碼和優化,是可以達到原生代碼的水平的,而 Android 則有很多問題,可是 RN 雖然有這樣那樣的問題,它對整個跨平臺開發是有很大貢獻的,讓開發者有機會多端開發,能涉足其餘平臺,並且相信將來這些問題都是能解決的。

相關文章
相關標籤/搜索