沈雅


達達集團是中國領先的本地即時零售與配送平臺,達達快送小程序作為公司重要的ToC業務入口,對外提供幫取幫送、跑腿幫買、幫排隊等服務,承擔了ToC業務較高部分的訂單量。對于小程序而言,性能至關重要。但是用戶打開率偏低,以微信平臺為例,打開率是指在小程序冷啟動的情況下,點擊小程序到首屏渲染完成的百分比。代碼包越大啟動耗時越長、白屏時間越久,用戶越可能因為失去耐心而退出小程序,在轉化率不變的情況下,訂單量越低。如果性能不達標會帶來以下兩點影響:
影響小程序流量獲取;
影響用戶體驗,進而降低用戶留存。
不同平臺小程序性能指標構成不盡一致,但主要有3個維度考慮:啟動性能、網絡性能、運行性能,其中網絡性能主要包含請求耗時及請求錯誤率,和接口質量以及用戶網絡狀況強相關,所以此次性能優化的重點放在啟動性能以及運行性能。
碎片化主要是指優化動作過于離散、不連貫無法形成體系。過去在啟動速度不達標時,也會按照小程序平臺開發者工具建議優化,額外也有些分包、縮包、圖片壓縮等動作,但有局限性無法形成合力,依舊存在無法解決的問題:
優化效果有限,無法達到“優秀”官方評測結果;
啟動速度在需求迭代中反彈;
過于定制化無法陳定并固化至后續需求研發中。
增加啟動階段的理解、利用好開發者工具、借鑒官方啟動優化實踐,通過常規動作大概率可以實現一段時間啟動性能指標。但要實現指標長期穩定,依舊存在問題:
指標監控體系缺失:小程序后臺雖然會提供性能數據,但數據散落在各個平臺,很難在業務迭代中持續關注并跟進。另外一旦指標波動,用戶第一時間就會感知體驗下降,而研發是“后知后覺”的。
業務需求迭代包體積增加:過去3個季度,達達快送小程序的發單場景由2個變成8個。
碎片化:“碎片化”的優化實踐,不成體系無法系統性解決性能問題。
團隊成員變動:相關文檔少,人員變動時無法有效沉淀。
本地開發中微信小程序提供了體驗評分工具它會在小程序運行過程中實時檢查,分析出一些可能導致體驗不好的地方,并且定位出哪里有問題,以及給出一些優化建議。支付寶小程序可以使用全息檢測。
除了本地開發參考官方后臺,評分工具是在本地運行小程序代碼時進行分析,但性能數據往往需要在真實環境和大數據量下才更有說服力。恰巧,小程序管理平臺開發者提供了大量的真實數據統計。
雖然不同平臺對小程序啟動耗時的各有一套定義,但往往也只是對啟動耗時終點定義不一致,小程序的啟動流程非常類似,大致會包含以下4步:
小程序相關信息準備;
框架資源準備;
業務資源準備;
加載渲染。
以微信小程序為例,啟動耗時指標統計的終點是生命周期onReady作為時間的結束,比較趨近于First Contentful Paint(FCP)指標。而支付寶小程序則更加趨近于Time to Interactive(TTI)指標。
FCP:首次繪制任何文本、圖像、非空白canvas或SVG的時間點。
TTI:度量從頁面開始加載到主要的子資源加載完成并且能夠快速可靠地響應用戶交互的時間。
啟動性能指標是個綜合結果體現,不同階段會受用戶、平臺以及小程序本身的影響。因此我們以支付寶平臺提供的全息檢測工具,梳理了各個細分階段影響因素。
setUp:應用初始化,主要是db查詢時間。
Update:查詢更新信息,對比差異。
offine:下載離線包。
waitLoadApp:解壓,等待渲染。
分析各個階段影響因素確定4個核心優化思路:
靜態資源優化,控制總包、主包、分包大小;
接口聚合,合并接口并盡可能串行改并行,縮短最短請求路徑;
啟動頁模塊調整,分析啟動頁功能模塊,在不影響業務流程前提下,優化交互方式,提升頁面渲染性能;
沉淀傳承,總結歸納優秀實踐,持續保持優秀性能指標。
如上文所述,資源包體積對小程序包下載、包解壓、代碼注入等的多個啟動階段都有不同程度的影響。而且小程序平臺對包體積大小也有明確限制。
所以資源包體積大小對啟動性能也尤為重要,因此選擇“靜態資源優化”作為第一步優化動作。
包體積優化
核心思路:從包加載時機和包體積大小2個維度思考。對應動作分別是:預加載、縮包、分包異步化。
預加載:主要指微信小程序平臺,通過平臺提供的預拉取能力,在不影響主包的情況下將分包加載時機提前,提升分包頁面的加載速度。
縮包
分包:將相對獨立的頁面和組件拆分到分包,可以解決主包體積受限問題。但隨著業務的迭代實際開發中會遇到主包即使頁面很少也會過大,經過長期的實踐沉淀出一套適合我們的分包策略。
無用文件、函數、樣式剔除:業務迭代無法避免會產生無用文件,小程序可以入lazy Code Loading:required Components(在開啟“按需注入”特性的前提下,“用時注入”可以指定一部分自定義組件不在小程序啟動時注入,而是在真正渲染的時候才進行注入),我們也可以用工具分析沒有用到的文件和依賴,進行刪除。
延遲加載:分包一定程度可以縮減主包體積,但也會遇到“插件無法實現分包處理”和“多業務的分包難以劃分”。所以采用了分包異步化(該能力目前只在微信小程序中有)使用componentPlaceholder占位,異步拉取分包加載,完成分包下載和注入后,將占位組件替換成真正的組件。
圖片資源處理
壓縮圖片:上傳的圖片可以使用2倍圖(當設備像素比很大時,圖片會被放大,而放大會讓圖片看起來模糊。為此,可以使用2二倍圖的方式來提高圖片的清晰度)并且壓縮,大圖片不要超過100 KB。利用一些壓縮技術對圖片進行壓縮,壓縮尺寸可觀,但對圖片顯示質量影響甚微。
小圖片使用IconFont:避免小圖片作為CDN的情況,因為HTTP一個域名下有請求并發限制,過多圖片會造成隊頭阻塞。可以使用IconFont和base64等方式。
大圖片統一使用CDN:在圖片處理中,因為遇到的情況是大部分都是運營配置的圖片,可以在配置的后臺限制大小,自動轉化成webp格式(webp它可讓網頁圖檔有效進行壓縮,同時又不影響圖片格式兼容與實際清晰度,進而讓整體網頁下載速度加快)。
懶加載圖片:屏幕外的圖片可以使用懶加載,只有下拉之后才去加載對應的圖片。
在小程序中,發起網絡請求是利用wx.request來發送的,不同于瀏覽器一個域名并發的限制,wx.request調用的并發限制為10。超出限制的請求會被阻塞。所以針對接口主要采取的舉措有:
實時性較低的接口緩存結果:可以采用的策略是,頻繁使用的數據緩存到內存,實時性較低的結果緩存到本地,下次啟動可以讀取到內存中繼續使用,也可以給緩存設置一個時效,更新本地緩存;
減少接口串行:接口沒有依賴關系的可以考慮使用Promise.all;
預拉取首屏資源:預拉取能夠在小程序冷啟動的時候提前向第三方服務器拉取業務數據,當代碼包加載完時可以更快地渲染頁面,減少用戶等待時間;
接口合并,優化速度:分析接口詢問后端是否可以合并接口,優化稍慢的接口;
小結:在分析接口層面時,我們整理了小程序首頁的接口調用的時序圖;
優化前:總共用戶進入小程序后21個(埋點接口和個端差異接口未計算在內);
優化后:總共用戶進入小程序后17個(埋點接口和個端差異接口未計算在內);
對比優化的結果由原來的串行長度為10加少到了6,接口總數減少4個,并加入了接口緩存,預拉取,本次優化在支付寶大約帶來了500 ms的時延下降。
不管是微信還是支付寶小程序啟動速度的考量并不單單是首頁的耗時,包含所有入口頁面。達達小程序有查看訂單詳情,活動頁等都會作為啟動頁投放。我們對業務梳理針對性的做了優化。
首頁
首頁預先拉取了金剛位,banner,廣告位等資源,因為實際調用中獲取這些資源鏈路比較長,我們冷啟動中獲取資源在小程序啟動直接渲染,在真實調用中去比較數據的變化,增量更新,雖然首頁是千人千面,但是絕大多數內容是類似的,提前渲染可以提升總體加載時間。
對于獲取位置信息是比較耗時的,在用戶授權后緩存了結果到內存,并且存入本地設置了一個時效,在用戶下次打開時,未過期的情況下可以直接讀取本地I/O。考慮到位置信息需要調用多個接口獲取且較慢,所以提升較大。
在分析包大小時發現主包存在過大的SDK,但沒有辦法去除,或者利用plugin方式加載。最后是將涉及插件的SDK拆到分包中利用分包異步化去加載。
首頁彈窗邏輯之前有5種以上,通過讓后臺承擔更多的業務邏輯,可以節省小程序前端代碼量,以前的做法是前端從不同接口獲取資源,然后通過優先級去展示,前端做很多邏輯處理,現在有后端實現了一套廣告位系統,減少了接口調用和邏輯判斷。
物流詳情頁
針對天降紅包,因為他串行3個接口,影響較大。我們執行了發券用戶操作之后去加載紅包。
針對地圖的展示,去除了非必要的路徑規劃,折疊展示了地圖;本次業務流程的優化,在支付寶小程序總體降低了400 ms時延。
“突擊優化”一定程度上能夠提升性能指標,但持續保持性能指標的穩定則功在平時。因此除了固化一些專項優化策略,比如:分包策略、圖片處理策略等,還需要沉淀更多實踐并傳承。
流程類:
建立指標監控體系:主要包含:包大小、圖片大小、啟動耗時、評測結果。過去研發對于指標的波動是“后知后覺”的,指標監控利于研發盡早人員發現問題,減少對用戶影響。
規范發版:除了代碼包體積代碼注入、首屏渲染之外,發版頻率等因素也會影響小程序啟動耗時,通過與業務方確認在不影響小程序正常功能迭代的前提下,約定每周2次固定發版。
代碼類:
要提升渲染性能就要了解小程序的架構,如下圖所示。

從圖中可以看出,由于渲染層與邏輯層分開,一個小程序有多個界面,所以渲染層對應存在多webview。這2個線程之間由Native層進行統一處理。無論是線程之間的通信、數據的傳遞、網絡請求都由Native層做轉發。所以如何減少數據交互的頻率和大小是提升渲染性能的關鍵。
代碼類:
合并setData調用:盡可能合并setData的調用,將多次setData合并為一次,可以模仿vue的nextTick寫合并的邏輯(調用setState時提供的對象會被加入到一個數組中,當下一次事件循環執行的時候再把這些對象合并一起,通過setData傳遞給原生小程序),支付寶小程序可以調用batchedUpdates來批量更新,減少調用次數。
減少setData大小:傳輸的數據量越多,線程間通信的耗時越長,渲染速度就越慢,我們的做法是將不設計渲染的數據放入_data中。
去掉不必要的屬性節點,綁定屬性數據大小:在xml中綁定屬性data-xxx,盡量減少數據的大小,視圖層會把事件target和dataset數據傳輸給邏輯層。當自定義數據量越大,事件通信的耗時就會越長。
JSAPI調用限制:一些同步API會阻塞渲染,啟動頁面減少使用Sync結尾的同步API,get SystemIn fo Sync,get Storage Sync,set Storage Sync。可以緩存結果,或者使用異步獲取數據。原則就是:減少使用,緩存結果。
依賴庫優化:刪除了埋點框架中一些非必要的同步API的獲取操作,或者延后執行,將頻繁調用的API結果緩存到內存中,減少調用。
我們來梳理一下整體的優化思路,如下圖所示。

微信平臺
啟動耗時降低20 %;
過去兩個季度,啟動耗時長期維持在優秀;
打開率提升0.5 %。
支付寶平臺
C端支付寶小程序【達達快送】啟動耗時降低40%,現在維持在優秀水平。
服務競爭力指數SCI(支付寶平臺):提升至滿分。
經過本次優化微信和支付寶小程序都達到了官方的優秀指標,優化的經驗適用所有小程序。但如何監控啟動速度,如何結合自身業務定義小程序性能指標,出現問題及時告警,能夠定位具體的原因,需要建立一套自己監控體系,持續保持小程序性能。