Weex & ReactNative & JSPatch

weixin_34208185發表於2017-12-13

遷移一批老文章到掘金

在微博上看到一篇絕讚的文章

Weex&ReactNative對比

絕對值得強烈推薦,我想寫這篇文章的原因就是因為看了這篇文章覺得太棒了!前一陣子我自己確實有比較深入的拆解分析ReactNative的原始碼,一連寫了三篇原始碼分析,當Weex開源的那一天,我也第一時間run起了demo,第一時間感受了這個東西,很多的新奇!很多的驚喜!

由於時間精力所限,我一直沒有深入的去拆解分析weex,還是比較初步的瞭解和使用,所以一直沒準備動筆寫一篇對比類的文章,直到我今天看到了這篇文章,基於對RN的理解,看到了作者這麼深刻的從方方面面非常多的角度對比分析了二者,簡直產生了好多共鳴!雖然我不瞭解weex,但是RN的痛點還是相當相當認同的~哈哈哈哈

於是才產生了今天的blog。

並且在這基礎上,我還想對比一下iOS 與 JSPatch的這套框架,因為無論weex也好rn也好,他們的思路或許一脈相承思路同源,但是和JSPatch這套方案比,那還真是完全不同的兩個世界,這兩個世界差異之大,但又能實現同樣的熱更新功能介面,對比一下真的很有意思!

Weex&ReactNative對比

這二者之間的對比我相信,讀過作者原文的能學到很多,這裡我就不重複囉嗦相同的內容了,不過我還是想以標註的形式,對於那些對比差異點我還是想說說我的一些個人的看法^_^

(後面的內容,豎線+灰色文字是原文,底下是我的看法)

JS引擎:

weex使用V8, ReactNative使用JSCore

這一點和作者交流確認了,iOS上weex依然使用JSCore而在安卓上weex使用了V8。

其實為什麼會這樣也是有原因的,安卓ReactNative雖然使用了JSCore,但這個JSCore不是系統源生的,而是直接打入app包裡的WebKit庫,這也是為啥安卓專案引入RN包大小會增大4~5M的原因,ReactNative在iOS上JSCore是系統自帶的,完全無法打入app包內,所以iOS的包大小,引入RN後變化並沒有那麼誇張。

來到了weex,反正安卓RN都是完全自己植入進去的一整個JSCore,那不如把JSCore換成最新的V8引擎,但是iOS就不同了,iOS繼續使用系統自帶的JSCore還是方便的,系統自帶的不用白不用,別浪費了,不然APP包憑空再多個4~5M,這也是個大槽點哈哈

vue vs react

react模板JSX學習使用有一定的成本 vue更接近常用的web開發方式,模板就是普通的html,資料繫結使用mustache風格,樣式直接使用css

我是iOS開發哈,前端領域我不是很瞭解,我是看原文評論裡,很多人說vue用起來更爽,我還沒深入使用,但是我一個iOS客戶端寫React和JSX還是有點痛苦的╮(╯_╰)╭,不過值得說的是即便使用了vue,聽說weex依然重現了react的virtual dom以及v dom diff演算法,作為渲染效能上的保證,聽說哈,我不懂,希望更多的前端大神給我解惑

佈局

兩者實現了flexbox的相同子集(都使用了FaceBook的程式碼解析),基本沒有區別

哈哈都使用了FaceBook的程式碼解析,weex不愧是站在巨人肩膀上的~,順帶FlexBox演算法這個還是好多都在用,聚划算的LuaView在用(lua的flexbox演算法),fb的AsyncDisplayKit Layout在用,我其實對這個沒啥好吐槽的,唯一隻有一點

我是一個客戶端開發,對於源生客戶端開發來說,flexbox佈局產生的頁面層級太多了╮(╯_╰)╭,雖然weex和ReactNative,實際上都是純native的介面渲染,但即便是native程式碼,View層級多了依然會帶來些許效能問題

而FlexBox的排版就產生了很多這樣的空UIView層級容器.這一層面其實是可以優化的,佈局用的View容器其實不用驅動UIManager親自繪製,不知道是不是我把問題想簡單了,大家可以看一下這圖,一個很簡單的頁面對於native來說,撐死了每個圖片是一層完事,但是換成了flexbox,層級就變成了這樣

其實我倒是有個解決辦法,就是不以前端的佈局思路去做js指令碼驅動native繪製,而是以客戶端的思路,依然用js指令碼驅動native繪製

這種思路的老前輩就是早已成熟3~4年的cocos2dx-lua,那種動態指令碼更新整個遊戲app,動態更新功能不是個新技術哈~思路早就有。

這種思路其實也是JSPatch的體現,JSPatch雖然寫的是JS程式碼,但是全都要以native的編寫程式碼風格,native的api使用風格去寫程式碼,最終寫出了跟純native一模一樣的功能介面。(相比較cocos2dx whole nativebridge的方案 JSPatch 的runtime bridge方案極度的輕便的多!)

頁面開發

weex提供了一個playground,可以方便的預覽正在開發的頁面

ReactNative開發一個頁面,需要建立一個native工程,然後編譯執行

這一點也是一個最大的感觸,weex是一套解決三端,安卓,iOS,wap的解決方案,而ReactNative彷彿在facebook創造它的時候,沒有為他考慮太多的wap,聽說攜程在使用RN的時候,先有ReactMix,後有Moles框架,都是在RN之上,在封裝了一層統一wap,iOS,安卓三端的龐大框架,現在好了weex從一開始設計的出發點,就從3端去考慮了

既然支援了wap,wap也是一個環境,自然寫功能頁面的時候,完全可以不用Android Studio,不用Xcode,開一個瀏覽器跑起來wap的效果,也就是wap playground,等wap調的差不多了,再搭建native環境去細緻跑

打包

ReactNative官方只能將ReactNative基礎js庫和業務js一起打成一個js bundle,沒有提供分包的功能,需要製作分包打包工具

weex預設打的js bundle只包含業務js程式碼,體積小很多,基礎js庫包含在weex sdk中

這個是個大吐槽!RN打出來的包,除了我們自己寫的index.ios.js以外,把一整個React基礎JS庫全打進包裡了,確實這部分完全可以內建app的,不然太浪費動態更新的流量了

擴充套件性

元件的擴充套件上,weex和ReactNative具有一樣的能力

三方庫的接入上,weex對網路,圖片,統計等常見的使用者可能想自己定製的功能,提供了相應的適配介面,可以由使用者方便的定製,ReactNative需要自己修改原始碼

這塊雖然不瞭解weex,但覺得ReactNative做的實在是太讚了,所有RN的native底層,都是一個個的模組Module,完全解耦,隨意靈活擴充套件插拔移除,而且支援開發者自行構建完全自己的Module,無論是介面還是資料網路介面。

我經常說的一句是,ReactNative其實是一個可以任意擴充套件支援的框架,你down下來的原始碼,你發現實現不了的功能,擴充套件一下,妥妥沒問題,你down下來的原始碼,你發現效能有問題?很卡?(沒錯我說的就是listview!)我去自己擴充套件一個啊~iOS基於tableview自己重寫一個帶重用cell的RNTableview~安卓基於recycleview,重寫一個哈?我始終認為,RN是一個思路,不是一個死的框架

Moudle方法呼叫執行緒

weex 可以通過註解標註是否在UI執行緒執行

ReactNative在native_modules執行緒執行

這一點和作者交流了一下,安卓是這樣的,但是iOS不是,iOS裡面,RN每一個native_modules執行緒一樣可以支援標記控制,只需要重寫module的methodQueue方法,return gcd佇列的mainqueue就好了,RN的module執行緒控制原始碼就是dispatch_async,gcd的queue一般預設用class那麼字串建立,所以預設是隨機執行緒唯一佇列,但如果指定async到mainqueue,是可以直接切換到主執行緒的

//RCTClipboard類的原始碼
- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}
複製程式碼

ListView Android

ReactNative目前採用scrollView使用,有一些效能問題

weex使用recyclerview實現,效能稍好

這個剛才就提到了的一個大吐槽!RN的ListView!RN的ListView是完全基於scrollview封裝的,一旦資料夠多,是完全沒有重用的,業務中如果真的對listview效能痛苦不堪,並且RN提供的一些JS層面的優化效能的屬性還不能滿足需求,那是真的得完全重新自己封裝一個基於native重用的ListView,很多人在使用RN的時候估計也都不能忍ListView然後自己封裝了吧?這下好了,weex幫你做了

2016.7.27補充

ListView缺少重用機制是一個本質上的問題

無數的人在和我強調ListView沒有問題,我這裡說的是重用機制,重用機制ListView是完全沒有的,所以ListView在使用上推薦使用分頁的方案,而不是無限制持續上拉載入更多,無上限的持續載入成百上千的Cell,這一點才是本質層面的問題

ListView由於這個底層實現的本質,所以在使用上推薦用分頁的方法,無論是20個一屏,還是30個一頁,實際上沒啥區別,依然是無法不封頂的增加數量,在一些常規使用方法下ListView有一些基本的優化選項,可以增加表現力,但這改變不了功能上缺失的本質

facebook官方文件中文翻譯-效能優化

這是一個頻繁出現的問題。因為iOS配備了UITableView,通過重用底層的UIViews實現了非常高效能的體驗(相比之下ListView的效能沒有那麼好)。用React Native實現相同效果的工作仍正在進行中,但是在此之前,我們有一些可用的方法來稍加改進效能以滿足我們的需求。

以上是FaceBook官方的原文翻譯,ListView有沒有問題自然能看到。不用我多說了。。。

順帶Git上已經有UITableView的RN封裝

github react-native-tableview

粗略看了下原始碼實現,跟我腦海中的那套方案一致,每一個cell內部是一個rootview,可以通過initParams傳入不同資料,進行reload渲染,cell自然進入native OC的重用池,(我太偷懶啦,有了一點思路,一直沒親自實現)

工具鏈

臥槽,我要繼續提一句,weex不虧是站在巨人肩膀上的,開發RN的時候其實有很多痛點,除錯介面的時候,我需要全域性加框,來檢視flexbox佈局是否正確,打包的時候我完全得自己重新開發分包或者diff包的工具,從而做到增量更新。

當我第一天使用weex的時候,就看到了那個devTools,那個可愛的小button,這可比RN的command + R的能力強大得多,用兩個字評價就是貼心!

說點我自己的對比看法

我還是想說

站在巨人的肩膀上看的更遠!

其實在我看來(我不太懂前端,我覺得從React.JS到Vue.JS,前端層面weex和rn的差異應該非常大),在native層面,也就是如何用js寫出來的確是純native介面這個原理上來說,Weex與ReactNative,思路同源,一脈相承,我覺得差異完全不大,原理是共通的,甚至如果扔掉了JSCore換成C++的luaengine,是不是跟LuaView都原理和核心思想是一致的呢?(luaview也是flexbox用lua寫佈局,最終是native渲染,不過我是真的沒親自跑過luaview的程式碼,但我2年前是做cocos2dx-lua的,非常瞭解lua動態更新app的機制)

但是facebook做出來的RN,我們在使用上確實是有很多的吐槽,這些吐槽完全不是RN做不到

  • 你說RN做不到封裝一個打包+diff工具嗎?不可能,咋可能做不到
  • 你說RN做不到開發一個更加貼心,更加酷炫的devTools和那個可愛的小button嗎?當然不可能,擴充套件一下分分鐘支援
  • 你說RN真的做不到更優化ListView?很多第三方的做出來了,facebook咋可能做不到
  • 你說RN真的做不到統一wap?傳說中的攜程的moles都是基於RN然後再擴充套件的一套牛逼的大框架,說明設計上沒啥不可能做到的

RN本身是一套非常牛逼非常靈活而模組擴充套件式設計,天然支援你任何開發者去補充RN,去擴充RN,這些都不是RN的不足。

如果把ReactNative比作是Linux系統,不懂的人會覺得這也太難用了吧?看視訊軟體需要單獨配置,去yum包去命令列安裝配置一大堆,裝各種軟體都得命令列弄一大堆(很久以前上學的時候純小白對Linux的認知),但不能說Linux系統是不能看視訊解碼小電影的╮(╯_╰)╭

那麼Weex就很人性化的針對痛點,做了更多改善上的事情,更貼心,更易用,更強大,但又不失靈活,開源,擴充套件都還在(這裡沒有引申windows缺點的意思)

我甚至能有一種感覺,一定是深刻的使用過RN,真的瞭解了RN的痛點,才驅動做出這樣一個貼心的專案

iOS 平臺獨有的 JSPatch 動態更新完整功能

這裡我要說一點就是,JSPatch現在很多人是拿他當HotPatch,進行熱修復線上bug去用的,但是他得益於OC極其強大牛逼的執行時,超級靈活的反射機制,不像安卓的hotfix,可能受限於裝置,受限於系統,總之反射機制不能完全發揮作用。

  • JSPatch可以修改任何已經用OC寫好的程式碼從而實現hotfix
  • JSPatch可以動態建立任何原本OC程式碼沒有寫沒有實現的類
  • JSPatch可以對任何類(無論OC已有程式碼的類,還是JSPatch動態建立的類)建立任何新的方法,任何OC原本沒有寫好的方法
  • JSPatch可以呼叫任何存在類,存在的方法,哪怕這條呼叫語句並沒有預先被OC寫好

從上面的描述我們可以看出來,JSPatch絕不僅僅可以用來HotFix,而是可以用來構建全新的功能,並且實現一整個功能模組的動態更新!

並且JSPatch的靈活無人能比

說句實話JSPatch得益於牛逼無敵的OC,runtime,他的靈活能力,是ReactNative&Weex這個方案思路遠遠不能比的。

  • 例子一

舉個最簡單的例子,RN與Weex,對於微信支付這種很native的模組,或許都是採用預先在native程式碼裡寫好了實現,最後讓JS去呼叫(無論是開發者自己擴充套件的還是系統擴充套件的),這個東西就叫jsbridge,但這個bridge很普通,你寫了微信支付的jsbridge,那你的js就有了微信支付的能力,你寫了支付寶支付的jsbridge,那你的js就有了支付寶的能力,你寫了iOS平臺的IAP的jsbridge,那你的js就有了IAP的能力,如果你沒寫咋辦?你的JS就辦不到,要知道,擴寫這些jsbridge,都是需要發版的,都是不能動態更新的。

換句話說,如果一開始使用RN的app只開發了支付寶的支付功能,寫好了支付寶的jsbridge,於是發版上線了,等某一天突然想接入蘋果的applepay,那麼只有一個辦法,那就是重新發版,重新寫好了applepay的jsbridge,再次提交蘋果稽核(我舉了個系統庫的例子,但意義可以擴充套件一下)

但是對於JSPatch來說,JSPatch也是一套基於JSCore的bridge,但是牛逼就牛逼在JSPatch不是這種常規的normal jsbridge,而是runtime jsbridge,runtime的特點就是實現沒有被任何oc程式碼預先寫好的功能,同時還能修改已經被OC程式碼預先寫好的功能!

  • 例子二

如果整個APP都是RNorWeex包一個殼子,所有功能都是js寫的,那麼如果不需要更新jsbridge,那麼確實可以實現,修改任何功能模組,寫新的功能模組,都可以用RNorWeex進行熱更新,完全不發版。

但是我相信,相當多的APP都是部分介面保留原有工程的native,部分介面,部分新介面採用RNorWeex(那些已經成熟的大app一定是這樣的,不可能推翻重寫)

那麼問題來了,RN能更新用RN寫的介面,RN能更新那些原本native的介面嗎?答案是不行,JSPatch可以。RN可以開發一整個新功能介面,動態更新到app上,但是這個新功能介面怎麼開啟呢?辦法有一個,app內有一套URLRoute的路由機制,並且輔助以雲端可控的路由配置表,那麼確實可以改變某些位置原本的介面跳轉,從而跳轉開啟全新的RN介面,實現了新RN介面的動態更新,但是JSPatch就不需要URLRoute這套全域性跳轉的輔助機制幫忙,JSPatch完全有能力更改任何已經由OC寫好的程式碼,隨意的改變跳轉到新介面,隨意的增加新按鈕,不改變舊介面就把新介面開啟!

阿里的Wax

與JSPatch能力和機制類似的還有阿里的Wax

Wax本是很久以前國外的大神編寫的一套用Lua寫OC的動態框架,但是這個框架太老了,老到還沒有支援蘋果64位就沒人維護了,年代太久遠了

大眾點評的大神在Wax的基礎上融入了執行時,變成了WaxPatch,但這年代也很久遠,也是在沒有支援64位的時候就不維護了。

直到去年10月,阿里宣佈接管維護了Wax,並且大量擴充套件了海量功能,因為以前的wax也好waxpatch也好,支援的動態runtime能力還特別少。

不過都是純iOS平臺,畢竟這種思路和玩法完全依託於無敵的OC Runtime

JSPatch 福禍相依,優劣共存

和JSPatch的作者@bang哥學習以及交流了很多動態更新方面的看法,bang哥最近也在醞釀一篇大作,專程去對比JSPatch與ReactNative的對比。

bang哥的看法

對比 學習成本 接入成本 熱更新能力 開發效率 效能體驗
Weex&RN 高,跨平臺
JSPatch 中,不跨平臺

總的來說,JSPatch在學習成本,接入成本,熱更新能力上佔優,而 React Native 在開發效率和跨平臺能力上佔優,大家可以根據需求的不同選用不同的熱更新方案。JSPatch 目前仍在不斷髮展中,歡迎參與這個開源專案的開發。

原文還沒釋出,等到釋出了我一定第一時間補上原文連結。

我的一些補充:

  • 學習成本:

這其實是對native開發人員來說的,JSPatch所有的API,所有的寫法,所有介面的佈局,都是純native開發人員最熟悉的,只是需要略微熟悉下從OC變JS的轉變(最基礎的js語法)(甚至有一些不是太智慧的oc直接轉js的工具),以及JSPatch的一些獨特規定,對於native iOS開發人員來說(就是我啦),學習css+html式的佈局,理解flexbox(忍受flexbox那層級的吐槽),其實遠沒有直接按著native的思維方式寫JSPatch方便。

ReactNative&Weex的方案更貼近web開發人員,尤其是熟練掌握React和Vue的,但是當面臨自定義擴充套件native能力也就是bridge開發的時候,其實還是需要native程式碼能力,並且視你的擴充套件需求不同,可能需要比較深的native能力。

所以從我一個native開發人員的角度來講,JSPatch學習成本低很多

  • 接入成本

你能想象 JSPatch的核心程式碼,不算邊角擴充套件,所有原始碼只有2個檔案嗎?jspatch.jsJPEngine.h.m

  • JPEngine.h.m 只有區區1700行程式碼!
  • jspatch.js 只有區區200行程式碼!

這接入成本可想而知啊,下原始碼,扔進工程,完事啊~哈哈哈哈哈

  • 熱更新能力

前面解釋的夠多了,在iOS平臺上,JSPatch其runtime能力,真的是無論weex還是ReactNative都是遠遠無法比擬的

  • 開發效率

確實Weex & ReactNative的輔助開發工具超級多,並且HTML+CSS式的介面佈局,熟練了以後開發真的是太快了╮(╯_╰)╭

JSPatch還是native的開發方式,輔助工具也開發了很多,大家可以細看JSPatch的Github和bang哥的Github和部落格,但效率上我覺得還是比不上的╮(╯_╰)╭

  • 效能體驗 bang哥的評價二者都是高,我覺得二者差異不大,但非要說個優劣,我提到過flexbox的層級問題,即便是純native開發搞出如此多的層級,也是會有效能損耗的,但這個點可能沒那麼關鍵,效能損耗沒那麼嚴重。二者最終都是純native的效果與渲染,與觸控互動,所以,都給了個高的評分

我用JSPatch也遇到一些坑

JSPatch目前在使用上會和webview有一定的衝突,如果有需要動態用jspatch寫一個含有webview的頁面的時候,會有略微的麻煩,和不穩定因素,在GitWiki上有明確介紹瞭如何在JSPatch裡面使用webview,比如初始化的時候先於JSPatch構建一個Webview然後在銷燬啊,比如js建立webview記得需要十分小心的使用一個performInOc的api啊,不要按著常用方法使用,還有就是不穩定,我的case是某一些特殊的業務場景下,會偶現crash(很低概率,但是頻繁操作可以復現)

JSPatch與ReactNative的記憶體控制差異

  • JSPatch在OC與JS互動的時候,是支援直接把一個OC物件,一個介面,一個model,直接傳給JS上下文裡面的,這個OC物件會因此引用計數+1,並且隨著JS上下問的垃圾回收機制,對這個OC物件進行引用計數的額外控制,在JS上下文內是無法去檢視這個OC物件的,但是卻可以指定任何OC物件原本的方法,然後傳送回OC環境去操作這個OC物件。

  • RN在OC與JS互動的時候,是完全不支援傳遞任何OC物件的,所有能在JS與OC中間傳遞的,一定是可以被json化,字元化的內容,數字,字典,陣列,字串,所以RN專門有個RCTConvert類去專門處理,json的序列化model化,反序列化反model化。那麼RN是如何通過JS去控制一個純OC的介面View呢?是通過viewTag,JS控制的每一個介面效果,都是傳過來一個tag,讓native建立,讓native修改,native會儲存住這些tag到一個hashmap裡,這樣JS才能夠不直接傳遞OC物件,而是傳遞一個數字,從而控制OC物件

二者的實現差異,是會造成一些底層執行差異的,OC與JS物件只傳遞JSON其實就保證了,JS上下文的記憶體與OC上下文的記憶體完全沒有互通,各自的內從各自控制,JS是一套垃圾回收機制,而OC是一套引用計數機制。

JSPatch將二者進行了互通,這些互通的物件記憶體管理則是一套,又有引用計數控制,又有JS的垃圾回收,當JS的垃圾回收,並且iOS的引用計數歸0,才會銷燬。

這裡沒有優劣之分,JSPatch在雙記憶體控制機制下,也是可以正常work沒有問題的,RN&Weex的這套機制,記憶體上簡單清晰,不過這都是底層實現的問題,上層使用,都是沒問題的

相關係列文章

相關文章