RN學習1——前奏,app插件化和熱更新的探索


react_native_banner-min.png

React Native(如下簡稱RN)有大量前端開發者的追捧。前端開發是一個活躍的社區,一直嘗試着一統先後端,作一個全棧開發,RN就是他們在客戶端領域的嘗試。javascript

說是從零開始,但其實我仍是懂一點點JS代碼的,並且算是一個有經驗的iOS、Android開發,對不少js和native交互的細節和特性還算了解,在QDaily裏面也作過好多hybird的嘗試,還常常用JSPatch作hotfix,總的來講,就是對hot update、插件化以及hybird編程很是很是感興趣。RN也許是已知的開源方案中最好的一個吧。css

寫在最前

先開始提供個思路,做爲一個移動客戶端開發(區別於前端開發),我使用RN的目的根本上是爲了插件化以及插件的線上熱更新,對於前端開發那種全應用RN化的雄心是敬謝不敏的,同事對這種方案開發全app的能力也是存疑的(後文會解釋緣由)。html

至於爲何要學習RN,主要是我的之後目標是作一個更加成熟團隊的客戶端負責人(or客戶端架構師),須要對整個客戶端橫向技術棧都要有本身的理解和認識,如今已經可以在Android和iOS方面有一些本身的認識,進入RN領域其實也是瓜熟蒂落的了。前端

在將來RN的學習和使用過程當中,我也會更加傾向於關於這種方案的內在原理、使用場景、使用邊界以及一些其它優缺點方面,實際使用中也會先在一些比較輕量級的場景使用。java

動態配置

客戶端的新版本都依賴用戶進行升級才行,如何可以第一時間讓用戶使用最新的版本是app開發者的永恆話題。node

若是採用後臺熱更新,不管採用何種方式,咱們的流程老是能夠歸結爲如下三部曲:「從 Server 獲取配置 –> 解析 –> 執行native代碼」。react

針對客戶端程序的混合開發很是有必要,能夠有效的進行app一些突發模塊的開發和處理。已知的通常思路包括:android

  • 一、簡單的js bridge方式,內容呈現採用H5,增長一些和native的交互。如今好奇心日報就是這個方案,雖然簡陋,但基本就是這個思路。上個東家微信在這方面也基本就採用了類似的方案,只不過在加密和安全方面作了更多的處理。ios

  • 二、後臺以zip包得形式下發html、css、js和相關png組件,下載以後將全部資源按照原有目錄結構放在app本地的一個http服務器中,app中得webview請求localhost的固定地址進行請求,這種請求會有AJAX跨域問題,通常只有native端徹底接管網絡請求可使用,例如微信春晚紅包就是這個方案。方案2基本是方案1的優化變種,因爲兼容性好,入門簡單,比較成熟。git

  • 三、以zip包得形式下發html、css、js和相關png組件,客戶端深訂製一套方案,可讓js使用一些原生的UI組件能力,比較典型的就是增長下拉刷新組件。這套方案在淘寶、支付寶普遍使用,須要進行學習和研究,一方面在部署架構,另外一方面是具體實現細節。阿里開源了其中的weex組件,基本思路也是這樣的。

  • 四、純js或者lua以patch的形式進行原生開發,經過反射調起原生代碼。這個方案在iOS下可行,Android下面還存疑。並且是比較重的客戶端耦合,好奇心日報如今用它進行一些緊急bug的處理。

  • 五、採用react-native進行混編,這種方案比較完整,就是原生native客戶端集成react-native組件,經過後臺訂製下發一些使用js和css編寫的資源模塊,經過react-native框架進行渲染解析,成爲原生應用。方案已經在天貓iPad客戶端的某些模塊上、QZone的某些使用,並且facebook的f8大會的app所有采用其編寫,其生產能力是不容質疑的。方案原理和方案三、方案4相似,只不過中間封裝了一個更加完善的中間件。

QDaily 的現有嘗試

一、基於css的ui配置方案。

基本思路是在css文件中定義ui組件的邊距、顏色、字體、大小、背景色等等與UI相關的內容,在代碼中經過宏(Android中用import static)進行UI渲染。

在app啓動時,將css文件load進入內存,保存成k-v的形式,具體UI代碼直接面對這些k-v數據結構便可。


Qdaily css架構.jpg

這種方法的好處是:

  • 一、在適配夜間模式或者一些固定屏幕版本時,只需增長一套css文件便可處理;
  • 二、並且,因爲修改資源文件,app不須要從新打包編譯;
  • 三、同時,這種方案能夠經過後臺配置新的資源文件,在運行時替換掉內存中的約定key下的value,從而實現線上條件下的UI調整。

侷限性也是很是明顯的:對native代碼的依賴太硬,能作的很是少,也就能改改樣式,與熱更新和熱修復都扯不上關係。

二、基於js bridge的bybird方案

基於H5的webview hybird方案算是在性能上作一些妥協後比較成熟的方案了。

在android和iOS部分各封裝一個js-bridge用於js和native的交互,至關於一箇中間件。該中間件包含一個native部分和一個js部分,兩部分溝通採用各自平臺的特性方案,iOS採用訂製request scheme並攔截request的方案,android採用@javascript的方案。經過中間件,前端開發者僅僅使用1套代碼就能夠兼容兩個平臺,兩個平臺各自暴露native方法給前端。

該方案實現的部分有:

  • 一、兩個js-bridge,用於交互。並以此約定標準化調用接口。
  • 二、爲webview發起的請求綁定cookie,以可以進行用戶識別
  • 三、爲webview發起的請求定製化UA,以區分瀏覽器仍是app,以及android仍是iOS。
  • 四、針對webview中全部資源(html、js、css、image)都進行本地的持久化,以提升訪問速度。

方案好處都能看見,缺陷也很明顯:效率太依賴機器性能以及瀏覽器內核(不過就算內核再好效率也是存疑的),同時針對原生部分的調用依賴於原生提供能接口,幾乎是每增長一個功能,native部分也須要對應開發一邊接口。

以上兩個方案各有特徵,但終究沒離開採用約定好的配置信息就行混合編程的路子。從本質上來講,就是移動端和服務端約定了一套協議,可是協議內容嚴重依賴於應用內提供的能力,不利於拓展。尤爲是方案1,只是在解析字符串,它徹底不具有運行和調試的能力。方案2的效率問題也很是明顯。

三、jspatch的熱修復方案

iOS7之後,系統中包含了jscontext進行js語言的解析,至關於從讀取配置文件到讀取邏輯一個質的飛躍。

jspatch將js代碼進行解析,並經過反射(invoker)調用objective-c的代碼,幾乎能夠作全部oc能夠作的事情(由於OC的runtime實在太強大)。

方案在QDaily中主要用於線上熱修復。這個方案也有其很差之處:一個是隻支持iOS,針對android仍是無能爲力;二是編寫頁面實在難用,難以調試,從整個生態來說,也都使用比較輕量。

react native在iOS端實現遠離和jspatch的遠離基本一致,同事結合了方案2中語法的一些特性以及方案1中的配置特性。相信是如今已知的最優解決方案。

混編

決定學習之初直接上混編,由於這纔是使用的目的,只有支持這個才具有插件使用的條件。

開一個官方demo——AwesomeProject,而後開始修改(如何安裝和配置請自行google,官方教程很詳細)。

一、OC調起RN

直接上代碼,咱們假設native頁面原本好好的,點擊了一個按鈕跳到了一個RN的活動頁面

- (void)viewDidLoad { [super viewDidLoad]; UIButton* startRNVC = [[UIButton alloc] initWithFrame:CGRectMake(20, 50, 60, 40)]; [startRNVC setTitle:@"Start RN" forState:UIControlStateNormal]; [startRNVC addTarget:self action:@selector(gotoRN) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:startRNVC]; } - (void) gotoRN { NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"AwesomeProject" initialProperties:nil launchOptions:nil]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; [self.navigationController pushViewController:rootViewController animated:YES]; }

二、OC中等待RN調起的部分

RN有比較完整的調用代碼,只要按步驟作就行了。關鍵是RCT_EXPORT_MODULE這個宏,會在class的load方法中進行register,這點和js-bridge的方案很像。

@implementation SpringBoard RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(gotoIM:(RCTResponseSenderBlock)callback) { AppDelegate* appdelegate = (AppDelegate*) [UIApplication sharedApplication].delegate; UINavigationController *controller = (UINavigationController*)[appdelegate.window rootViewController]; CDLoginVC *loginVC = [[CDLoginVC alloc] init]; [controller pushViewController:loginVC animated:YES]; callback(@[[NSNull null]]); } - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } @end

三、RN部分調起Native

RN還不是很理解,就把代碼都貼上來了。這裏會在頁面啓動時候直接alert出來,點擊會再跳回native部分。

'use strict'; var React = require('React'); var RN = require('react-native'); var { Image, AppRegistry, ListView, StyleSheet, Text, View, AlertIOS, } = RN; var styles = RN.StyleSheet.create({ container: { flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', } }); function setup(): React.Component { AlertIOS.alert( 'Foo Title', 'My Alert Msg', [ {text: 'Foo', onPress: function FooClick() { var SpringBoard = RN.NativeModules.SpringBoard; SpringBoard.gotoIM((events) => { }); }}, {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, ] ) class AwesomeProject extends React.Component { render() { return <View style={styles.container}> <Text>This is a simple application.</Text> </View>; } } return AwesomeProject; } AppRegistry.registerComponent('AwesomeProject', setup);

總結:事實上,效果很好,輕鬆實現了混編和調用,考慮到RN在調起Native部分須要OC進行代碼定製化編寫,因此將來考慮增長其與jspatch的協做,加強其能力;android部分還須要繼續研究,相信不是問題(QZone已經在進行相關的研究和應用了)。

學習計劃和曲線

我我的是一個雙平臺開發者,同時對hybird編程比較感興趣,也作過一些研究和嘗試,因此RN中關於平臺接口部分、原理以及js-native交互部分學習是比較平緩的。但我javascript只是一點三腳貓功夫,更別提ES六、React一個有一個生僻而又讓然懵逼的名字,還有node.js等等神同樣的存在...這部分估計學習要很是陡峭。

本着先難後易的原則,學習部分會優先進行ES6標準的基本語法和習慣開始,而後經過改造QDaily一個模塊進行React和RN的熟悉,在其中不斷學習f8的代碼和使用方式,並在過程當中將RN完全融入原有app項目中。

這過程可能須要一本基於ES6的javascript的書籍,一套比較權威的RN教程和文檔,f8的代碼以及針對其的解讀,還有若干大牛的博客和社區。

本文結束

mark一下本文的參考文獻,以及可能要學習的一些東西:



文/野火wildfire(簡書做者) 原文連接:http://www.jianshu.com/p/97c2fa718ee4 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。
相關文章
相關標籤/搜索