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