因爲一些緣由,筆者最近變動到了RN的團隊,迴歸到了hybrid app的開發的圈子中,當然是有蠻多新鮮感和新機遇的,不過遙想起之前在hybrid中各類view以前跳轉的頭疼等各類問題,筆者懷着忐忑的心情開始了一段波折的hybrid之旅。其實大概的結果以前的文章也有說起了,不過因爲大部分只是以「記筆記」的形式描述的,因此可貴想抽個時間,好好的總結一下,本身的心路歷程。前端
衆所周知,傳統的webapp因爲只能發揮native80%不到的機能,在性能和能力上一直爲人所詬病,而傳統的native app又須要耗費大量的人力,在這個互聯網發展速度「過快」的時代,不多會有公司會持續這樣「雙份」的投資,因此hybrid app在一開始便以更高的性價比得到了業界的青睞,特別是進入react時代以後,react-native一經問世,便受到了業界大量的關注,然而幾年過去,react已經更新到16.x,內部也迎來了極大的重構,fiber的推出,異步組件的提出,生命週期函數的unsafe,react彷佛正在朝着愈來愈強大的方向不斷進步,而react-native卻依舊在0.5x.x的版本使勁掙扎,這裏面卻有一些不得不說的苦楚。java
RN升級的辛酸node
由於筆者團隊維護了多個rn的app,可是因爲一些歷史緣由,兩個app幾乎是獨立的,投入度高的app rn版本能到0.4x,而另外一個則僅有0.35.x,可是RN在升級的過程當中,每每是伴隨着一些破壞性更新的,不少舊的api在新的rn版本中是得不到支持的,但不少機型的兼容性問題或者說一些新的特性又只有新版本的RN纔會提供,這也致使了RN升級的這件事是必需要實施的。也許對前端的同窗來講,升級不就是換一下package.json的版本號就行了麼?但實際上卻至關的複雜:react
一、首先,因爲xcode自己開發上的一些設計,無論是第三方庫仍是官方庫,都有經過pod或者經過Library的方式來引入,可是Library中的庫中的有關引用的寫法,隨着版本的迭代大部分都不支持了,並且它那種組織方式自己就不是一種可複用的組織方式。android
並且,在調試中你還須要不停地本身手動的清除xcode的緩存,xcode自帶的clean並不會清除它的構建緩存,這將致使,你對組件的變動,可能根本沒有生效!webpack
解決方案:在iOS中,修改Library中有關庫的引用方式,將它們所有遷移到經過pod來管理git
二、而後,propTypes和createClass二者雖然早起都集成在react的包中(固然RN的包中也有集成),可是隨着版本的迭代,二者都被移出的react的主包中,和react-dom同樣,須要開發者手動引用,然而對於一個5w+行的項目,組件量達到300+,要進行這樣的變動,是一件很是痛苦的事情。。github
解決方案:只有手動的逐行變動代碼,使用alias的一些小花招會令你的代碼可讀性變差web
三、接着,你會發現,以前一直使用的fb提供的官方組件也沒有了。。並且官方明確地表示,他們都被廢棄了,須要開發者本身去引用react-native-deprecated-custom-components,好比範用度特別高的Navigator,可是爲何官方會廢棄它們呢?筆者的我的觀點是:「官方也沒有提供一個令他們滿意的解決方案」。好比剛剛提到的navigator,由於其自身在android上無可避免的會出現狀態丟失的問題(雖然這是android的內存管理機制引發的),可是做爲結果是,navigator就必定會致使在android上出現view狀態丟失的問題,並且這種問題,無論是單vc仍是多vc的環境下都會產生,且沒有很好的修復辦法(雖然從理論上來說,推行全app的類redux狀態管理器能夠解決該問題),因而官方變廢棄了該類組件。json
解決方案:針對本身的業務場景,本身實現本身須要的功能,筆者的團隊就採用了fork官方組件,再二次實現的方式(固然,最終仍然須要狀態管理器來解決根本的android的bug)
四、而後,當你修好了官方組件的bug,以爲你的模擬器將要跑起來的時候,第三方的組件又開始大量報錯了,固然,處理了上述那麼多問題的你,這時已經想到了緣由:由於react的版本和rn的版本都發生了大量的迭代了,其相關組件也跟着須要破壞性更新也是很正常的。可是現實是並非全部的第三方組件做者都會持續更新。。因此,若是做者還在維護這個組件,那麼你是幸運的,若是做者已經沒有維護了,那麼,要麼你能在社區找到可以替換它的組件,要麼你還得fork對應的組件,本身進行改動,而且成爲維護人。
筆者也是如此碰到了一些使人頭疼的問題:在筆者團隊維護的一個app中,使用了一個歷史比較悠久的第三方組件,而筆者的團隊由因爲一些緣由,沒有持續使用fb提供的packager或者metrobundler進行打包,而是使用了webpack,這致使了原做者可能並不瞭解前端的模塊規範和打包方式,他寫的組件能在metro中跑起來,可是當環境切換到webpack時。。整個組件卻不能正常編譯了。。
另外好比不少早起的組件在android中都會重載createJSModules的方法,可是到後續的rn版本中,該方法變成了虛方法,不須要重載直接實現就行了,而這些組件的維護者彷佛都沒有繼續維護了,就須要使用者本身去fork組件來修改java代碼。
解決方案:第三方組件,3分依靠做者對本身組件的責任心,7分依靠整個生態的熱度,剩下90分,都得靠本身。
有關升級筆者碰到的問題大概就是這些了,固然這是除去IDE上使用的一些坑(小問題筆者便按下不表了,上文也只提到了嚴重影響筆者使用的地方),鑑於上文也提到了有關rn打包的事情,筆者接下來再聊聊rn打包的一些感覺。
RN打包的一些感覺
最開始筆者並不瞭解問什麼fb非要從新造一個packager去進行打包,由於rn歸根結底也不過是把寫的jsx編譯成一個es5的jsbundle,依賴對應平臺的jsc來執行,一樣是「打包」,爲何不使用webpack呢?也許是因爲rn本身的特殊性吧?筆者如是想,可是無論是packager仍是後來的metro bundler,作的事情幾乎和webpack是同樣的,並且他們都是用了babel社區的ast能力,去解析jsx文件,就連最後的注入require和__d的定義也和webpack注入webpack_require同樣,爲何呢?也許是fb的工程師們在着手作這件事的時候,webpack的文檔和社區並不像今時今日這樣的強大吧?因而也致使了metro造了一個和webpack幾乎相似的輪子,並且它的文檔和它的前人同樣差!且因爲生態和環境相對閉塞,metro bundler的文檔嚴重滯後了不少個版本。
話說回來,筆者之因此要更換打包工具,主要緣由也是由於要作一些拆包的事情,可是因爲metro-bundler自身生態的閉塞,且文檔確實少得可憐,而業內對於rn拆包打包仍是有一些例如haul這類基於webpack的實現的,因而筆者也借鑑了社區的一些智慧,進行該實現。
可是每每理想很美好,顯示很殘酷。雖然webpack的生態已經很成熟了,可是在使用webpack來打包rn的時候,仍是不可避免的會碰到一些問題,好比:
一、編譯環境的不一樣。由於webpack針對的不只是是給瀏覽器的,還包括了node端,因此會帶有一些編譯平臺所自由的方法或者類庫,好比說console、Math、Date、crypto,可是rn因爲是在native環境運行的,而fb一開始考慮的場景就和傳統的web應用不徹底同樣,筆者在進行打包工具切換的時候就遭遇了本來metro可以運行的庫可是在webpack的編譯環境中會報錯。
解決方案:對於編譯環境的不一樣能夠嘗試使用webpack的target屬性爲webworker,這個屬性設置的時候會更貼切rn的編譯環境
二、圖片處理的不一樣。rn對圖片有個比較貼心的處理,它可以自動經過@2x @3x去自動適應2倍和3倍屏的圖片,可是本來的webpack是不支持的。
解決方案:本身編寫一個loader去解析圖片
三、一些全局變量。相信進行rn開發的同窗或多或少都會使用debugger,可是debugger會依賴rn掛載在全局的一些變量,好比require和__DEV__,而這些變量在webpack裏則須要經過providePlugin或者其餘的方式注入。
另外,筆者的項目因爲一些歷史緣由,像比較流行的嚴格模式都會引發項目運行報錯,還有一些json文件的讀寫,也是metro的編譯比較友好(或者沒有傳統前端嚴格,好比json文件裏的註釋),而webpack的相關loader則不會讓一些這些「友好」的寫法經過編譯。
總的來講,其實大部分使用RN的同窗可能和筆者都會有一樣的想法:
「本來美好的write once,run anywhere,但現實是write once,fix everywhere,並且根本無法fix完。」
其實rn的問題或者說特性還有不少,像那個依賴setState通訊的動畫,主線不斷推送的事件,還有不少單平臺纔有的屬性,都是rn開發的障礙, 因此就筆者而言:目前的rn更適合一些有native能力的團隊在一些並非很要求app性能的條件下使用的解決方案,在沒有通過適當優化的基礎上其性能表現並不必定有webapp好。