標籤:

Qreact,去哪兒網的迷你react方案

去哪兒網在React Native深耕多年,對React內部實現的了解在國內應該是非常領先的。迫於項目對React體積的極致需求,我們推出了自己的迷你化方案——Qreact。

Qreact比市面上的其他迷你react框架的實用性更強,基本上可以說無縫切換到任何已有的react 15工程中,能極大地改善對體積的壓力,這在對流量非常苛刻的移動端上尤其重要!

我們從今年1月份快速啟動項目,在1個月內大致完成了功能,Demo,並配合現有的複雜例子進行驗收。本文將分享我們在做這輪子過程中的一些想法,包括競品分析,實現思路,項目風險控制等等。

一、核心需求

我們並不是無事找事,為造輪子而造輪子。雖然有KPI的成分,但它的核心需求是來自業務線,可以說就算我們不造,公司內部其他人也會造一個,但那個質量可能無法保證,畢竟公司絕大部分的高手都分配到我們事業部。

由於沒有產品經理,我們需要充當產品經理的角色,聆聽業務線的需求,自己挖掘需求。

去哪兒深耕React多年,構建了兩個基於React的UI庫,它們都是用於移動端。如果這些庫都是內置在APP中,應該沒有要求。但是去哪兒分成十來個事業部,根據事業部的賺錢能力分配更新包的體積。為了能讓用戶在wifi上更新我們的APP,更新包的體積一般不超過100MB,因此像這樣公用的框架與庫體積越少越好。

此外,其中一個UI庫是用於手機瀏覽器上,我們稱之為React Web,用戶每次打開我們的頁面,都會載入一遍React與相關組件,這個對體積就更加敏感。因此當我們完成React Web,就著眼於迷你React的開發。

這個新的框架有三個核心需求:

1、體積小。移動端對體積一向敏感,因此在jQuery時代,zepto能割踞一方。

2、支持事件系統,這不是簡單add Event Listener,是React原來的那套Synthetic Event。它幫我們搞定300ms延遲,還有滾動列表時誤觸發點擊的問題。如果你不用它,你需要讓業務線參照iscroll的原理自造一個。

3、能直接替換。換言之,新框架與原框架的功能幾乎一致。因此許多業務已經

用React開發完畢,不希望做太多改動。由於業務線有時時間趕,碰到難題搞不定,會傾向用一些怪招歪招。在無法預料對方用什麼API的情況,新框架框架覆蓋原React的各種偏門用法。

下面是一個緊急修復的補丁:

圖1

我們列舉一下各種偏門的API與用法

1、mixin包含mixin。這個在RN很常見。

2、ref, setState傳函數的用法

3、context與getChildContext的運用,雖然官方明確不建議大家用,但是著名的react-redux在源碼里用到了。

4、_rootNodeID, _hostParent,_hostNode這些內部屬性用在後端渲染與事件系統中。

二、競品分析

圖2

在立項後,我們開始找市場上的同類產品,如果有滿足的,我們就不用開發了。目前,前端要找這些框架,只有一個去處,就是GITHUB。這是開源界的寶庫,應有盡有,琳琅滿目。

由於代碼公開,大家可以抄抄,因此每流行一樣的東西,大家都是一窩蜂上的。除開那些純練手的項目,每個庫都有自己獨到之處。

自從React推出虛擬DOM來解決複雜應用的性能問題以來,GITHUB上有上百個虛擬DOM的庫,包括之前的angular, vue2都在底層使用這種性能利器。

圖3

這是一些虛擬DOM框架或庫的數據,從相似度,性能,流行度,版本更新等情況綜合考慮,我們也只能選上面三者:inferno, preact, react-lite。

inferno從各方面來看,是無可挑剔的,性能比排行第二的kivi快20倍,更不用說vue,angular什麼之流。每個庫都會吹自己的框架有多快,但inferno的主頁上有大量測試頁面,是有真實數據支撐的。但是它偏面追求性能,源碼里的可讀性太差。看不懂,無從入手,只能遺憾地放棄了。

其他性能流有citijs, snabbdom, virtual-dom。最早搞出性能引擎的是citijs,然後基於它上面分化出kivi, ivi, snabbdom,然後vue2.0又直接將snabbdom庫整合到它裡面。virtual-dom則是走另一種性能優化方式。但它們都是迷你庫,API與React差太遠。

於是只剩下preact與react-lite。

三、設計思路

由於是業務線的迫切需求,並且拖得越久,就越多項目用上RN,到時需要回歸測試的項目就越多,因此必須儘快搞出來。我們就不打算重造輪子,而是在已有輪子上改改。

第一版是基於react-lite。這是因為react-lite是攜程的工業聚大神寫的,攜程是我們的兄弟公司,應該比較好交流。但現實中發現,這個庫的擴展性不足,比如說事件系統那裡,需要傳入4個參數,在react-lite里只能拿到三個參數,想盡方法也無法湊齊第四參數。還有一些內部屬性,渲染流程與原裝React差得太遠了。在雙方折騰了2個星期後,我們組有人心灰意冷,著手後備方案,preact。

preact比起react-lite多出幾個優勢:

1、官方提供兼容補丁preact-compat

2、插件巨多

3、ISSUR活躍,當天提問題,大概到晚上,外國人起床就有回應了。

4、擴展方便

尤其第4點,在開發qreact時,我們都為雙方提了不少ISSUE。其實程序員還是比較靦腆,不願麻煩人,因此我們寫框架時還是多留一些擴展介面吧。

整個qreact的架構大概就是:

qreact= preact改+preact-compat改+react-web事件系統迷你版

在preact的源碼里一個叫options.js的文件,裡面有一個options的對象,它會被框架的多個關鍵方法調用。我們通過為它重新實現某些方法,就達到改寫框架的目標。

github.com/developit/pr

圖4

兩個react-lite的難點問題,由於options的擴展機制太靈活了,一下子被擺平。

1、事件系統需要傳入4個參數的問題。在options添加一個handle Event方法。

2、內置屬性問題,在options重寫vnode方法。

重點說一下內部屬性問題:

圖5

隨著版本的升級,這些內部屬性越來越多,這裡講解一下其中三個:

圖6

這了讓preact支持它們,我們是在框架diff節點時,重新添加上它們的。因為這時,我們能輕鬆知道一個節點在DOM樹的上下關係。

最後是對事件系統進行瘦身。React有16000行,其中10000行都是事件系統相關的。再加上React Native中的Pan Responder系統。這體積非常龐大。但是如果我們將要支持的瀏覽器收窄一點,不支持IE系列與firefox系列。起碼在事件對象的構造器上,我們可以做一些合併操作。

下面React中的事件構造器列表:

facebook/react

它們濃縮成一個事件構造器後,代碼少了3000行。

我們再對事件插件進行圍剿。因為我們不需要mouseenter, mouseleave, input, composition,beforeinput的兼容,又可以減少許多行。

facebook/react

最後成果是 qreact縮少到6000行,事件系統占其中的4000行,min後的體積為39kb。原版React的min體積是140kb。減少近80kb。

體積算是達標了,那麼性能如何呢?畢竟我們使用React的初衷是因為它的性能太好了。React的性能主要來自它的虛擬DOM的diff演算法。體積縮水了,它的diff演算法肯定也打折扣。這時preact提出兩個後備方案:

1、減少要比較的虛擬DOM的數量 hydrate。這是發端於inferno的優化方案,通過合併相鄰的字元串或數字,減少虛擬DOM數量。

圖7

2、減少要生成的真實DOM的數據recycle。上面的hycycle也會減少真實DOM的數量,但我們還可以將要移除的真實DOM保存起來,重複利用這些真實DOM。

通過這兩種機制,大大彌補qreact diff演算法的缺憾。此外,我們還可以通過動靜分離的方式來提高性能。在定義JSX時,我們就能得知某個元素是否包含花括弧,有花括弧說明其是動態的,反之是靜態的,但一個元素與其所有子孫都沒有花括弧,那麼這個子樹可以整體緩存起來,以後轉換為真實DOM後,它能緩存起來。

然後在組件的render方法中,對於這部分的React Element每次返回相同的對象,並且在上面添加一個標記,碰到兩個對象都有這個標記,就直接返回,不往下比較了。這是inferno提出的另一個性能優化方案。

最後驗證性能是用ListView進行測試的,和原來一樣流暢。

四、分享展示

aHR0cDovL3VlZC5xdW5hci5jb20vcXJlYWN0 (二維碼自動識別)

圖8

裡面最重要的兩個例子就是yo-demo與qunar-react-native-web

【作者簡介】鍾欽成,網名司徒正美,著名的JavaScript專家,去哪兒網前端架構師。在GITHUB擁有複數個著名的輪子,著有《javascript框架設計》一書。本文來自司徒正美在「攜程技術沙龍——新一代前端技術實踐」上的分享。

沒看夠?更多來自攜程技術人的一手乾貨,歡迎搜索關注「攜程技術中心」微信公號哦~

推薦閱讀:

React.js: web開發者的14個工具和資源
React Fiber初探
通過 JSX Control Statements 編寫 JSX
為何需要Angularjs、backbonejs、reactjs?
Vue2技術棧歸納與精粹

TAG:React |