React-Native 目前是一個不錯的多平臺開發和快速開發的解決方案,它解決了不少開發上的痛點,而且做爲 facebook 大廠出品,維護上有保障,所以學習 RN 是有好處的。固然,它目前還存在着不少的問題,可是均可以經過一些方案來規避和解決。RN 目前來講對 iOS 開發者比較友好,而 Android 開發環境則比較難以配置。node
學什麼都得先來個 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
{ "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 環境配置比 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 類來作封裝,而且將參數拆成了好幾個類,很是的麻煩。
手動 iOS 整合步驟以下:
package.json
建立帶有 subspecs 的 Podfile
用於引入所須要的第三方庫,這須要 CocoaPods,雖然也能夠手動 link 第三方庫,可是目前筆者推薦使用 CocoaPods
index.ios.js
RCTRootView
固然,還有其餘步驟,好比 ATS 的問題,不過一個合格的開發者應當都能完成,看上面這些步驟,你會發現實際上在 iOS 下,手動整合和 HelloWorld 基本是一致的。
手動整合步驟以下
package.json
增長 com.facebook.react:react-native:+
和 maven {}
到 build.gradle
index.android.js
ReactRootView
是否發現了,在 Android 環境下,HelloWorld 工程和手動整合指南是不同的,最主要的區別在於 HelloWorld 工程是繼承 ReactActivity 來產生 Activity 的,而手動整合則是能夠隨便作,甚至能夠在 Fragment 上面建立 RootView。
就目前而言,RN 在 iOS 下不管是表現仍是編碼難度,都優於 Android 環境,Android 官方甚至沒有一個很好的啓動 App 方式,只能 react-native run-android
。並且在性能方面,iOS 通過良好的編碼和優化,是可以達到原生代碼的水平的,而 Android 則有很多問題,可是 RN 雖然有這樣那樣的問題,它對整個跨平臺開發是有很大貢獻的,讓開發者有機會多端開發,能涉足其餘平臺,並且相信將來這些問題都是能解決的。