滴滴出行小程序體積優化實踐

桔妹導讀:在19年下半年,爲了將微信錢包/支付寶九宮格入口的滴滴出行遷移爲小程序,團隊對小程序進行了大量的功能升級與補全。在整個過程中也遇到並克服了一系列問題和挑戰,其中包體積問題尤爲突出。接下來全面介紹一下滴滴出行小程序在體積控制方面做的努力與沉澱。



1. 
背景

微信對小程序包體積的要求是總體積不得超過20M,主包及單個分包體積不得超過2M。支付寶對於小程序包體積的計算方式雖和微信略有區別,不過整體也大同小異。


18年至19年初時,滴滴出行小程序裏承載的業務只有網約車,且業務需求較少,在主包內都能夠搞定。而在下半年時,爲了將微信錢包/支付寶九宮格入口遷移至小程序,小程序開始新增諸如公交/代駕/車服/單車/順風車等衆多業務線,同時網約車的業務需求也要做全面的補齊,業務量和代碼量一起爆炸式增長。


滴滴出行包含了豐富多樣的出行業務,包含了快車/專車/出租車/豪華車/拼車/單車/代駕/順風車/公交/車生活等衆多業務線。整個滴滴出行小程序的最重要,使用最高頻的頁面是首頁與訂單詳情頁,首頁中承載了各個業務線的需求表達,各個業務線的訂單詳情頁則承載了具體的出行訂單展示邏輯。此外還有各種功能頁面比如個人中心,營銷頁面,設置,歷史行程。


按照滴滴出行的產品邏輯,所有業務線的需求表達邏輯都在首頁承載,爲了良好的切換體驗,在首頁採用了單頁頂導的方案進行業務線展示。即每個業務線在首頁中提供一個需求表達組件,當用戶切換頂導業務線後,切換出對應的業務線組件。


在這種設計下,所有的業務線的需求表達邏輯都集中在首頁這個單一頁面中,導致在業務迭代過程中,承載首頁的主包體積迅速增長,很快觸碰了小程序平臺的單包2M上限,對後續的業務迭代與發展帶來巨大阻礙。因此,對於包體積的控制是我們在小程序開發過程中面臨的一大難題。



2. 
體積控制

下面我們將介紹滴滴出行小程序開發迭代過程中,我們對於小程序包體積進行的一系列優化控制實踐。


基礎優化手段

對於小程序來說,基礎的包體積優化手段包括:資源壓縮/去除代碼冗餘/資源CDN化/異步加載。

在web開發中,webpack提供了大量的代碼優化能力,包括依賴分析、模塊去重、代碼壓縮、tree shaking、side effects等,這些能力可以方便地完成資源壓縮和去除代碼冗餘的工作。滴滴出行小程序基於滴滴開源的小程序框架Mpx( https://github.com/didi/mpx )進行開發,Mpx框架的編譯構建完全基於webpack,兼容webpack內部生態,天然可以使用上述能力對包體積進行優化。


小程序中支持部分靜態資源(如圖像視頻等)使用CDN地址加載,我們會盡可能將相關的資源壓縮後放到CDN上,避免這部分資源對包體積的佔用。


小程序場景下無法像web當中通過script標籤便捷地進行異步加載,但是小程序平臺後期紛紛支持了分包加載的方案來實現該能力,由於分包加載是小程序特有的技術規範,webpack無法直接支持,因此Mpx框架專門針對該技術規範進行了良好的適配支持,關於該能力的應用我們會在後文詳細闡述。


除此之外,Mpx框架還針對小程序場景進行了許多包體積優化的適配工作,如儘可能減少框架運行時包體積佔用(壓縮後佔用56Kb),對引用到的頁面/組件按需進行打包構建,聲明公共樣式進行樣式複用,分包內公共模塊抽取等。


在Mpx框架的這些能力的支持下,基本不需要額外配置就能構建出一個經過初步優化的小程序包。


微信開發者工具選項裏也有類似的"上傳代碼時自動壓縮混淆"可勾選,但在開發者工具中上傳代碼時計算體積是直接計算的當前項目代碼的體積,並不會依據壓縮後的體積。因此,如果你使用原生小程序進行開發,你的source代碼極有可能進行進一步的壓縮以節省空間。


分析體積


雖然框架已經提供了很多在體積控制方面的優化,但是隨着業務迭代我們發現主包體積依然偏大。


在遇到主包體積偏大後,我們需要弄明白,主包裏有哪些東西?它們爲什麼這麼大?


使用原生小程序或者其他非基於webpack的框架進行開發的同學遇到這個問題後,可能只能去看硬盤上的文件大小。這樣一來,各個模塊的大小佔比可能並不直觀。而我們則可以藉助 webpack-bundle-analyzer 這樣一個webpack插件去做輔助分析。


比如這是一個使用Mpx框架編寫的demo,通過 npm run build --report 就可以看到這樣一個界面:



可以看到這個demo工程由 moment / lodash / socket-weapp / core-js 等第三方庫組成。各個庫的大小,相互依賴關係也能清晰地看出。


對於滴滴出行小程序也是能看到類似的圖,能看到整個項目到底是由哪些代碼組成。


另外,滴滴出行前端開發一直是採用“源碼編譯”的,可以讓整個項目裏公共的依賴可以實現僅有一份,一起共用。簡而言之,也有助於減少項目代碼體積。(相關資料:https://github.com/DDFE/DDFE-blog/issues/23)


要完美髮揮源碼編譯的效果,需要上下游一起建立整套源碼編譯生態,比如主項目的依賴方在聲明公用依賴時,就應該用peerDep或者devDep來聲明一些公有依賴,這些共有依賴應該在主項目中統一聲明,避免因版本不同裝出兩份公共依賴,那樣反而會增大體積。由於滴滴出行小程序涉及業務線及團隊衆多,部分團隊可能並不知道這個事情,因此代碼裏實際是可能出現上述劣化場景。而依照分析圖,可以容易地發現這種問題,並推動相關團隊清除這些重複依賴。


同時,我們依照體積分析圖,對其中體積較大的文件重點分析,進行了一輪業務代碼梳理和精簡,刪除了一些無用代碼,精簡了websocket的消息體描述文件等。


配置分包


分包是小程序給出的類似web異步引入的一個方案,把一些初始進入時不需要的頁面可以放進分包裏,跳轉到對應頁面時纔去下載分包,將這些頁面及其附屬資源放到分包裏可以有效減少主包體積。


Mpx框架早期對分包規範進行了初步支持,資源訪問規則保持和微信一致,主要根據資源存放的目錄判斷應該輸出到主包還是分包。有這個能力後,我們把行程頁抽到了分包,大概抽出了200多K左右的空間。


有了行程頁的成功拆分後,我們開始對所有的非首頁代碼進行分包操作,比如起終點選擇和個人中心。以及部分業務線的接入是通過npm的方式接入,我們也儘可能將這些業務線的所有非首頁的代碼放到了分包。


這裏還有個題外話,得益於mpx早期設計了packages形式的業務組合方案,可以很方便地讓業務獨立開發,又能及其方便地整合。而後發現微信的分包的json配置設計和packages很像,就在這個基礎上支持了微信的分包,用戶側僅需在原來的packages基礎上加一個query標記這個分包的名字即可。


拆除各個分包後,整個項目結構大概如圖:



初階的分包工作進行完畢後,總計從主包裏拆了差不多400K的空間到分包裏。


分包資源精細化管理

上面提到,Mpx框架初期的分包處理規則是完全按照微信的方式,把在分包路徑下的資源收集到分包裏。而npm管理的資源因爲都在node_modules目錄下,不屬於任何分包路徑,則會被全部收集進主包。


比如之前我們有行程頁分包,行程頁自有的狀態管理store整個都在行程頁分包的路徑下,就會被收集到行程頁分包中。而行程頁還用到了封裝好的didi-socket庫,這個庫是公共的npm包,即使它只在行程頁分包裏被使用,但由於它本身路徑是在node_modules下的,那麼就會將其收集進主包裏。


因爲早期的一些設計,行程頁的資源和首頁是分割開的,都比較獨立地存在於各自的路徑下,一期的分包處理的大頭也主要是行程頁,它剛好契合了Mpx初期對分包處理上的特點,因此能較好地收集進行程頁分包裏。


隨着業務迭代,後續大量業務線的接入都是通過npm進行的,就會有大量npm包資源,他們都在node_modules目錄下,因此全部會被收集進主包。


所以Mpx框架進行了一系列改造:


  • 在構建的依賴收集過程中,我們會對收集到的依賴打上標記,記錄它是被哪些分包引入的。一旦它只有一個分包引入,它就會被輸出到這個分包中。

  • 我們會根據用戶定義的分包配置,自動在 SplitChunksPlugin 中生成各個分包的 cacheGroups ,把分包中的複用模塊抽取到分包下的bundle中。

  • 對於組件和靜態資源,如果他們被多個分包所引用且未在主包中引用,爲了確保主包體積最優,這些資源將產生多份副本分別輸出到對應分包中,而不會佔用主包體積。


這樣一來,不管分包中引用的資源原本在什麼位置,最終輸出時都會盡可能將其輸出到dist的分包目錄下,避免佔用主包空間。


這個改動完成後項目結構看似和之前一樣,但得益於Mpx處理分包資源能力的升級,我們得以將業務線分包中引用的npm資源成功輸出到其所在的分包目錄下。


封面方案

再後來滴滴出行小程序需要替換微信/支付寶裏原有的WebApp入口,小程序接入的業務線迅速增加,包體積迅速增長。


這個部分體積增長的主要原因前面提到過,所有的業務線都要接入到主頁來展示。這也是由於業務特點決定的,滴滴出行提供了豐富的出行產品線,包括快車/專車/出租車/豪華車/拼車/單車/代駕/順風行車等產品,用戶是可能需要反覆切換挑選的。這個過程還要保留起終點車型之類的信息,必須是一個頁面內切換組件加一整套非常複雜的大型狀態管理才能比較流暢順滑地實現。而不能像一些電商/信息平臺,將不同的功能拆分到不同頁面,讓用戶通過首頁的菜單進入子頁面再進行操作,首頁只承載入口,只有較少的業務邏輯,分包處理起來就會容易很多。


因此各個業務線都要提供首頁組件進行接入。這個組件會在首頁被用到,所以無論如何也拆不到分包裏。最終,整個首頁主包部分的體積可以分成兩個部分:基礎庫和業務代碼。兩者的體積佔比大概是公共依賴基礎庫佔1M左右,業務代碼佔1M左右。


這麼龐大的基礎庫體積主要是由於滴滴出行的業務線及業務團隊衆多,各方均有一些自己的基礎依賴。比如網約車依賴的長鏈接通信pb數據描述文件,地圖會依的大數計算庫,順風車依賴的CML框架運行時、代駕依賴的通信網關庫,以及公用的組件庫和polyfill等。


所以滴滴出行小程序面對的問題在當時已經無法用純技術方案在短期內快速解決問題了,於是我們做了一個工程架構調整,可以叫封面頁方案,解決了主包問題。


封面方案簡單講,就是做一個帶滴滴出行Logo的封面作爲啓動頁面,而頁面一旦加載,立刻跳轉另一個頁面,這個頁面真正承載業務,且它被放在分包裏。


這個操作的意義在於,主包裏就只剩下了所有方都要依賴的基礎框架/庫等,而業務全被抽離到了分包裏。



這是封面方案完成後項目的結構圖,之前很大塊的首頁業務邏輯被抽出到首頁分包中了。


這樣一個挪移的操作的結果是我們可以有2M的主包空間來乘放基礎的公共的庫,有一個2M左右的分包來乘放前面提到的滴滴出行的集成了各種業務的“大主頁”。而當時拆下來差不多有1.2M的主包,800K+的業務主分包。


這個改造最優秀的一點在於,後續的業務迭代產生的體積增長几乎全是在業務主分包裏,剩下的1.1M+空間留給業務迭代還是比較充裕的。而主包的體積在理想條件下是可以長久保持不變的,就不會因爲業務需求的不斷開發反覆導致主包體積臨近超標,不再需要爲主包體積感到焦慮。


當然,可以看到,這個方案本身是沒有消減任何體積的,只是把位置變換了一下。除此之外,這個封面頁方案其實也存在一些缺陷,比如首屏業務的展示會變慢,因爲要加載的內容會變多,不過小程序本身有較好的緩存資源的能力,因此還算可以接受。


相比於因體積問題卡住需求迭代以及產品線的接入,目前這個方案至少能解決有無問題。我們開發團隊後續也會持續跟進關注體積問題,看是否會有產品方案變更或者小程序本身給出一些解決方案來進一步優化這個部分。



3. 
總結

Mpx框架在包體積控制上做了大量工作,對於npm場景下的小程序分包進行了非常完善的支持。


滴滴出行小程序團隊在框架支持的基礎上,通過梳理業務依賴,充分利用分包,調整交互方案等一系列手段,在不阻礙業務發展的前提下,將龐大複雜的滴滴出行小程序包體積控制在平臺限制範圍內。


希望本文能給在包體積上遇到問題的小程序開發者們帶來一些啓發,歡迎留言交流。



開源項目推薦:MPX



Mpx是由滴滴開源的一款致力於提高小程序開發體驗和開發效率的增強型小程序框架,通過Mpx,能夠高效優雅地開發出具有極致性能的優質小程序應用,並將其輸出到各大小程序平臺和web平臺中運行。GitHub地址:https://github.com/didi/mpx,歡迎體驗。



本文作者



延伸閱讀


內容編輯 | Teeo

聯繫我們 | [email protected]

本文分享自微信公衆號 - 滴滴技術(didi_tech)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章