ReactNative 開發實踐
序言
這是一個在StuQ做分享時的講稿。當然還有很大的改進空間,但我這會卻沒有什么勁頭繼續改進,既然這樣就不如索性先發出來,先把它從腦袋清出來,把其它我更想做的事情做了,回頭再看哪天想改了再回來改。
當然,大家有覺得不對的地方,隨便批,我會當做問題之一,以后努力改進:)
分享環節:

大家好,我是深圳平安科技的利炳根。網名:清醒瘋子。主要研究iOS和ReactNative。今天和大家分享的是我在ReactNative上的一些開發實踐經驗。請大家多多指教。
在ReactNative上的研究,我還是做得比較粗淺,有不對的地方,大家隨便批,我多向大家學習。

今天的內容分為四個部分。分別的ReactNative的開發環境搭建、JS/Bundle的管理、混編以及數據流的處理。

ReactNative的開發環境搭建比較簡單。只需要把node、watchman、flow通過brew安裝完,通過npm把ReactNative命令行安裝到全局,就可以用init命令創建ReactNative項目。
其中,node是node.js。它有非常好用的包管理工具npm。也可以拿來寫后臺。我自己經常拿來在本地快速寫一些簡單的后臺輔助調試代碼。
watchman是非死book的開源項目,主要用來監視文件,記錄文件的改動。React通過watchman來實現代碼發生變化時完成相關的重建功能。
flow也是非死book的開源項目,用來做JavaScript的靜態類型檢查。用來發現JS程序里的類型錯誤,提高編碼效率和代碼質量。包括前期錯誤檢測、代碼智能提示等。
安裝ReactNative命令行的時候要注意加上-g這個參數,把RN命令行安裝到全局,才方便我們在任何目錄通過命令行創建ReactNative項目。
ReactNative項目創建的時候,會自動把一些需要用的庫加到項目工程里,自動做好基本的依賴管理,并且自動生成最簡Demo代碼。創建完之后就可以運行一個最簡單的Demo,方便大家體驗ReactNative的編程。

目前,ReactNative還沒有特別好用的開發工具。開發效率會稍微打點折扣。但利用現有的開發工具還是足夠完成基本的開發任務的。
Nuclide是非死book的開源項目,是與Ract配套的IDE。目前還在不斷地完善當中。已經能實現基本的代碼提示和靜態類型檢查。Nuclide目前還不是獨立的IDE,需要作為Atom的包進行安裝。
Atom和WebStorm都可以做到React的部分代碼提示。對ReactNative的支持就相對差一些。主要還是針對JavaScript的支持而已。
ReactNative的JSX只是JavaScript的封裝。一些在JavaScript上的打包工具,ReactNative也能用。比如Browserify和Webpack。
Browserify使用比較簡單。指明入口點和出口點就可以打包。而Webpack則一般要寫一個配置文件webpack.config.js指明打包細節。當然,Webpack的功能相對就更強大一些。
Nuclide的開源項目代碼提交還是比較活躍的,對這方面有興趣的朋友不妨關注一下,參與其中和大神一起做一個IDE也不失為一種相當不錯的娛樂項目。

雖然ReactNative最終會渲染成Native的View,但它只能通過瀏覽器調試。基本上就是用Chrome來做。
Chrome上有一個插件React Developer Tools,不過國內或許不能訪問才能裝。
通過Chrome的consle可以打印變量。如果對這塊不熟悉的話,可以先通過調用一個網絡請求,打印返回的數據,并根據數據調整顯示,來熟悉一下Chrome的調試過程。
在JavaScript代碼中插入debugger;就可以作為調試過程中的斷點。

在iOS平臺上,ReactNative組件通過RCTRootView這個類接入到Native的視圖層次里頭。
RCTRootView初始化只需要提供兩個參數,一個是JS包的路徑,一個是模塊的名稱。
ReactNative的應用組件,最后渲染出來都是一個View的子類,都可以通過addSubView加到Native的視圖層次上面。

ReactNative可以實現動態編碼。只需要在遠端修改JS文件并保存,Native端無需重新編譯,只需要Reload刷新一下,就可以更新APP視圖和功能。
在設置RCTRootView的JS包地址時,設置為遠端的JS文件資源網址,在引用時把文件后綴改為.bundle,即可以調用遠端的ReactNative bundle命令,把對應的JS文件打包為可用的JS包文件,在Native端實現視圖的渲染。
bundle命令有兩個常用的參數。platform參數指定打包到iOS平臺還是Android平臺。dev參數指定是否開啟開發選項以提供更多的調試信息。

調用遠端JS文件打包的方式有一個缺點,打包過程導致視圖初次加載比較慢,影響用戶體驗。可以過加載離線包的方式解決這個問題。
在設置RCTRootView的JS包地址時,設置為Native端沙盒內的jsbundle文件地址,ReactNative就可以從Native端加載渲染視圖,速度有明顯提升。
通過bundle命令,可以把JS文件打包成jsbundle包。bundle命令有三個常用參數。platform參數指定平臺,entry-file參數指定入口文件,bundle-output參數指定出口文件。

通過加載Native端離線包jsbundle文件,在提升加載速度的同時也讓ReactNative失去了遠端更新的能力。這樣得不償失。我們可以通過靜默下載jsbundle文件替換Native端jsbundle文件的方式解決這個問題。
通過遠端jsbundle文件資源網址,啟動一個下載文件請求。在收到請求返回的時候設定文件路徑。在收到返回數據時寫文件。
寫文件時,要注意先讓寫入點移動到文件末尾,再進行文件寫入,避免覆蓋之前接收到的數據。

ReactNative因為技術發展不久,還有部分Native端的功能并沒有完全實現。在項目開發過程中有可能需要在ReactNative項目里加入Native端的功能代碼輔助。
ReactNative有一套機制實現。在Native端的類里引用RCTBridgeModule,并遵循RCTBridgeModule協 議。通過宏RCT_EXPORT_MODULE向JS暴露Native端的類,通過宏RCT_EXPORT_METHOD暴露Native端的方法,通過 RCTResponseSenderBlock實現JS端的回調。

JS端的處理和Native端同樣簡單。在JS文件的開始引用NativeModules組件。通過NativeModules引用Native端指定類。由Native端指定的類就可以直接調用指定的方法并指定回調的實現。

Native項目添加ReactNative模塊稍微復雜一點。首先需要進行一些工程配置。把ReactNative需要的靜態庫加到工程里。在 Build Settings里設置Other Linker Flags 和Header Search Paths。最后還要在Info.plist里設置App Transport Security Settings -> Allow Arbitrary Loads。

在Native的類中引用RCTBridge和RCTEventDispatcher。通過bridge的eventDispatcher向JS端派發事件。派發事件的接口有5個,分別對應5個不同的事件類型。
這里要注意,不要在Native端的viewDidLoad方法里向JS發送事件消息,因為這個時候ReactNative組件還沒有掛載出來,接收不到消息。

JS端在文件一開始引用NativeAppEventEmitter組件。通過NativeAppEventEmitter組件的 addListener方法監聽Native端派發的事件消息。組件卸載時,通過NativeAppEventEmitter組件的 removeAllListeners方法移除監聽。

ReactNative的數據流處理,目前業內有4種方案。原生的ReactJS通過Props - States - Components的機制處理數據流。
缺點很多。擴展性差,增加新功能時,需要在每一個用到的組件里復制一遍代碼。
可讀性、可維護性差,代碼量大,難以調試、測試。
可移值性差,代碼耦合度高,視圖、數據不分層。
違背單一數據層原則,在視圖層直接進行數據操作。

非死book官方推薦的Flux,通過Actions - Dispatcher-Stores - Component的機制處理數據流。
代碼冗長,Dispatcher和Store有很多重復煩瑣的人工檢查、定義。
多個Store中處理同一個Action,將產生大量的waitFor方法處理Store的先后邏輯。
多個Store間共享一個State,State有可能被分散到多個Store中,State的變化將變得不可控。
多個Store互相依賴,還有可能產生依賴循環。

Redux通過Actions -Store - Components的機制處理數據流。
Redux針對Flux做了不少優化。通過內部拓展actions的行為,移除了單例的dispatcher。stores可以監聽actions的行為,無需進行冗雜的switch判斷。
stores可以相互監聽,可以進行進一步的數據聚合操作。waitFor方法被連續和平行的數據流所替代。
Redux也有自身的一些弊端。由于整個應用的數據都集中在1個Store里,數據和處理邏輯的復雜度將不可避免地變高。
因為語法相似,從ReactJS、Flux遷移到Redux成本較低,建議舊項目遷移采用Redux。

Baobab通過Actions -Baobab Tree - Components的機制處理數據流。
Baobab的優點十分明顯。應用所有狀態都存在一棵數據樹里。可以非常簡單地實現數據域的切換。應用狀態樹的存在使得數據流管理變得非常簡單,代碼量少。
通過Baobab庫創建狀態樹,數據以鍵值對的方式存儲。
Baobab通過select方法直接切換數據節點,還可以通過cursors創建數據游標組,訪問數據。
Baobab通過set方法修改數據。
如果項目新建,沒有舊代碼維護的包袱,個人建議采用Baobab來處理數據流,一定會更省事省力。
結束語:
我的分享到此結束,非常感謝大家今天來參加,希望上面分享的內容能對大家有所幫助。如果大家對ReactNative或者其它問題想跟我交流,歡迎到我的支付寶經費群來。

問答環節:
1.能說說選擇相較PhoneGap,ionic等些其他平臺,為什么選擇React么?PhoneGap是比較早期的技術,一直以來,在性能上 或者功能的覆蓋上,大家都不夠滿意。尤其是性能。RN因為最終是渲染成Native的View ,體驗上就好很多。同時呢,因為RN是非死book在推,從Github上的Star,大家也可以看出業界還是對非死book有很大的期待和信心 的。
2.請問rn中的組件出現相互嵌套的問題可以怎么解決?即 a require b, b 也require a。當調用a時,就會b=null了。可以用es6的模塊解決嗎?這就是涉及到數據流的處理。如果用原生的ReactJS去處理,是會非常麻煩的。 非死book希望Flux能解決這個問題,但實際下來,大家覺得滿足不了需求。然后業界就有了Redux和Baobab兩種方案。Baobab是最新 的方案,解決方式也最優雅,大家有興趣可以重點研究一下。
3.既然是在Mac下,為什么不用safari調試,當前safari調試窗口也是很棒的在我自己這邊主要是習慣問題。一直以來用的都是Chrome。因為很早之前調試API啊各種東西都是用Chrome搞。既然Chrome能搞定就懶得換了。
4.Native與js調用時,與hybrid方式的app有什么不同。RN的雙向通信還是做得比較好的,上面的分享也有講到,實現起來非常簡 單。我在這上面多花了一點時間,也只是因為把發消息的代碼放到了AppDelegate上,結果死活收不到。Hybrid,常見也是用Bridge。這塊 的麻煩是,需要HTML5的同事配合。
5.再請教一個問題。組建多層嵌套時,組建間該如何通信?特別是最底層跟頂層的通信,除了props之外,還有啥好辦法嗎這個其實就是第四部分數據流的處理問題。Baobab可以把數據統一到一個地方,而且數據域切換非常方便,可以看看。
6.提問:React Native對安卓的支持情況,是否能兼容如此碎片化的安卓目前,我們還沒有去處理具體的適配。這塊因為主要是Android的同事在研究,我也不大清楚。不過,既然很多大廠都有用RN,應該不會有太大問題。
8.沒有ios經驗,只有reactjs經驗的,怎么上手xcode下react native開發配置呢,或者android下的Native下的工作量是非常少的。基本上只要隨便找幾篇入門的文章看看,問題就不大了。遇到搞不定的問 題多上stackoverflow搜搜。或者找我交流也可以:)
9.rn在開發團隊中的分工如何,如界面,js,ios程序員如何合作目前我們是1個iOS和1個Android,然后就是后端了。其實重點的開 發量反而是JS上的。一旦把平臺需要配合的東西摸清,剩下的主要工作量就是寫JS了。兩個平臺的多數組件都是可以共用的,也就是很多代碼只要寫一份就可以 了。
11.rn適合渲染view還是處理各種邏輯呢,有不如原生的地方嗎目前為止,我還是學習RN就算是View上也做得沒有Native漂亮。如果 邏輯比較復雜,是不是適合全部用RN來做,還有待考察。RN目前主要的針對點還是迭代比較快的相對輕一點的業務。反正熟悉之后,混編是非常簡單的事情,大 不了拿Native做一個復雜業務的API:)
問題回答完畢:)
招聘環節
招聘:深圳平安科技移動開發架構師團隊,iOS/Android/Java/HTML5/測試/運營都招。15薪,過節費豐厚,內部福利眾多,歡迎大家自投或推薦。簡歷直接微信發給我就可以了,盡量用PDF文件。