企業(yè)微信端產(chǎn)品“C端用戶小程序”欢顷,是一款服務(wù)于vivo線下代理铺峭、門店和導(dǎo)購墓怀,幫助導(dǎo)購連接用戶,快速與用戶進(jìn)行溝通的工具卫键】模基于“C端小程序”的PU/UV量較為龐大,為了更加極致的用戶體驗(yàn),所以提升小程序性能優(yōu)化是必然钓账。
一碴犬、業(yè)務(wù)現(xiàn)狀
1.1 存量用戶運(yùn)營企業(yè)微信“用戶端小程序”
存量用戶運(yùn)營企業(yè)微信“用戶端小程序”主要是服務(wù)于vivo代理、線下門店和導(dǎo)購梆暮,幫助導(dǎo)購連接用戶的工具服协。
- 用戶群:vivo線下用戶。
- 主要功能:vivo新機(jī)預(yù)售啦粹,新用戶注冊(cè)偿荷。
看下“用戶端小程序”的“新用戶注冊(cè)”,“新機(jī)預(yù)售”的頁面唠椭,如下:
vivo新用戶注冊(cè)
vivo新機(jī)預(yù)售
1.2 性能數(shù)據(jù)
一圖勝千言跳纳,看下存量用戶運(yùn)營“用戶端小程序”在沒做優(yōu)化之前,從“小程序數(shù)據(jù)助手”中獲取到的“代碼包下載量贪嫂,打開耗時(shí)分布寺庄,網(wǎng)絡(luò)請(qǐng)求延遲,網(wǎng)絡(luò)流量消耗”等數(shù)據(jù)力崇。如下:
從圖中我們可以看到斗塘,下載小程序代碼包主要集中在2-5秒,此外餐曹,部分http請(qǐng)求接口的時(shí)間延遲很長,會(huì)影響到整體頁面的渲染效果敌厘。由此可見台猴,存量用戶運(yùn)營“用戶端小程序”還有很大的優(yōu)化空間。
二俱两、性能指標(biāo)
2.1 怎么定義高性能饱狂?
單純的快是不行的。我們不應(yīng)該單純考慮速度指標(biāo)而忽略用戶的感知體驗(yàn)宪彩,而應(yīng)該全方位衡量用戶在使用過程中能感知到的與應(yīng)用加載相關(guān)的每個(gè)節(jié)點(diǎn)休讳。
2.2 性能指標(biāo)關(guān)鍵術(shù)語
FCP:白屏加載結(jié)束
FMP:首屏渲染完成
TTI:所有內(nèi)容加載完成
2.3 我們優(yōu)化需要達(dá)到的指標(biāo)
小程序官方指標(biāo):
- 首屏?xí)r間不超過 5 秒。
- 渲染時(shí)間不超過 500ms尿孔。
- 每秒調(diào)用 setData 的次數(shù)不超過 20 次俊柔。
- setData 的數(shù)據(jù)在 JSON.stringify 后不超過 256kb。
- 頁面 WXML 節(jié)點(diǎn)少于 1000 個(gè)活合,節(jié)點(diǎn)樹深度少于 30 層雏婶,子節(jié)點(diǎn)數(shù)不大于 60 個(gè)。
- 所有網(wǎng)絡(luò)請(qǐng)求都在 1 秒內(nèi)返回結(jié)果白指。
存量用戶運(yùn)營”用戶端小程序“需要達(dá)到的指標(biāo):
- 首屏?xí)r間不超過 2.5 秒留晚。
- setData 的數(shù)據(jù)量不超過 100kb。
- 所有網(wǎng)絡(luò)請(qǐng)求都在 1 秒內(nèi)返回結(jié)果告嘲。
- 組件滑動(dòng)错维、長列表滾動(dòng)無卡頓感奖地。
三、小程序的一些基本概念
3.1 小程序底層框架
小程序的最終渲染載體依然是瀏覽器內(nèi)核赋焕,而不是原生客戶端参歹。啟用了雙線程模型:
- 視圖層:也就是webview線程,負(fù)責(zé)啟用不同的 webview 來渲染不同的小程序頁面宏邮。
- 邏輯層:一個(gè)單獨(dú)的線程執(zhí)行 JS 代碼泽示,可以控制視圖層的邏輯。
3.2 小程序的啟動(dòng)步驟
1. 準(zhǔn)備運(yùn)行環(huán)境蜜氨。
- 在小程序啟動(dòng)前械筛,微信會(huì)先啟動(dòng)雙線程環(huán)境,并在線程中完成小程序基礎(chǔ)庫的初始化和預(yù)執(zhí)行飒炎。
小程序基礎(chǔ)庫包括 WebView 基礎(chǔ)庫和 AppService 基礎(chǔ)庫埋哟,前者注入到視圖層中,后者注入到邏輯層中郎汪,分別為所在層級(jí)提供其運(yùn)行所需的基礎(chǔ)框架能力赤赊。
2. 下載小程序代碼包。
3. 加載小程序代碼包煞赢。
- 在此階段抛计,主包內(nèi)的所有頁面 JS 文件及其依賴文件都會(huì)被自動(dòng)執(zhí)行。
在頁面注冊(cè)過程中照筑,基礎(chǔ)庫會(huì)調(diào)用頁面 JS 文件的 Page 構(gòu)造器方法吹截,來記錄頁面的基礎(chǔ)信息(包括初始數(shù)據(jù)、方法等)凝危。
4. 初始化小程序首頁波俄。
- 在小程序代碼包加載完之后,基礎(chǔ)庫會(huì)根據(jù)啟動(dòng)路徑找到首頁蛾默,根據(jù)首頁的基礎(chǔ)信息初始化一個(gè)頁面實(shí)例懦铺,并把信息傳遞給視圖層,視圖層會(huì)結(jié)合 WXML 結(jié)構(gòu)支鸡、WXSS 樣式和初始數(shù)據(jù)來渲染界面三幻。
3.3 小程序開發(fā)工具——體驗(yàn)評(píng)分工具audits
(ps:小程序開發(fā)者工具的評(píng)分插件audits可以對(duì)小程序的性能意敛,使用體驗(yàn)旋廷,實(shí)踐函荣,UI樣式,http請(qǐng)求等多個(gè)維度進(jìn)行綜合評(píng)分浸踩,建議小程序開發(fā)者在項(xiàng)目開發(fā)中使用叔汁。)
四、小程序優(yōu)化技術(shù)方案
4.1 針對(duì)小程序啟動(dòng)太慢的方案
方案1:無用的文件,函數(shù)据块,wxss樣式剔除码邻,不需要的import砍掉。
方案2:減少代碼包中的靜態(tài)資源文件另假。
除了部分圖片必須放在代碼包(譬如網(wǎng)絡(luò)異常提示)之外像屋,建議開發(fā)者把圖片、視頻等靜態(tài)資源都放在 CDN 上边篮。
方案3:邏輯后移己莺,精簡業(yè)務(wù)邏輯。
盡量把業(yè)務(wù)邏輯寫在后端戈轿,如果一旦出現(xiàn)線上問題凌受,小程序發(fā)版需要騰訊審核,而后端可以及時(shí)發(fā)布代碼思杯。
方案4:分包加載與分包預(yù)下載胜蛉。
方案5:部分頁面h5化。
小程序提供了 web-view 組件色乾,支持在小程序內(nèi)訪問h5誊册。如果小程序源碼太大從而影響下載時(shí)間,可以考慮降級(jí)處理暖璧,把部分頁面 h5 化案怯。
具體可參考web-view文檔。
4.2 針對(duì)小程序白屏?xí)r間過長的方案
小程序的白屏階段:小程序代碼包下載完(也就是啟動(dòng)界面結(jié)束)之后澎办,頁面完成首屏渲染的這一階段嘲碱,也就是 FMP (首次有效繪制)。
影響白屏的兩個(gè)因素:
- 網(wǎng)絡(luò)資源加載時(shí)間浮驳。
- 渲染時(shí)間悍汛。
方案1:啟用本地緩存捞魁。
- 將請(qǐng)求接口中獲取到的數(shù)據(jù)存儲(chǔ)在storage里面至会,部分?jǐn)?shù)據(jù)不需要每次發(fā)送http請(qǐng)求獲取。
方案2:跳轉(zhuǎn)頁面時(shí)預(yù)拉取谱俭。
- 一般是在頁面onload的時(shí)候去獲取接口數(shù)據(jù)奉件。
- 可以在調(diào)用wx.navigateTo之前先調(diào)用下一個(gè)頁面的http接口,將數(shù)據(jù)存儲(chǔ)在全局的promise里面昆著,下一個(gè)頁面onload的時(shí)候县貌,直接從promise獲取數(shù)據(jù)。
(ps:在A頁面onHide或者onUnload的時(shí)候通過promise請(qǐng)求下一個(gè)頁面B頁面的http接口凑懂,在B頁面onload或者onShow的時(shí)候從promise中獲取數(shù)據(jù)煤痕。)
方案3:非關(guān)鍵渲染數(shù)據(jù)延遲請(qǐng)求。
- 將頁面分為主體模塊(骨架,列表數(shù)據(jù))和非主體模塊(彈窗等)摆碉。
- 非主體模塊的數(shù)據(jù)請(qǐng)求可以延遲加載塘匣,使用setTimeout來請(qǐng)求接口。
方案4:分屏渲染巷帝。
- 將非主體模塊分屏渲染忌卤。
- 如下圖,我們將A模塊設(shè)置為主體屏楞泼,B,C模塊設(shè)置為非主體屏驰徊,等A模塊渲染完成后在渲染B,C模塊。
方案5:骨架屏堕阔。
- 可以使用尺寸穩(wěn)定的骨架屏棍厂,來輔助實(shí)現(xiàn)真實(shí)模塊占位和瞬間加載。
方案6:限制http請(qǐng)求數(shù)量印蔬。
- wx.request (HTTP 連接)的最大并發(fā)限制是 10 個(gè)勋桶。
- wx.connectSocket (WebSocket 連接)的最大并發(fā)限制是 5 個(gè)。
方案7:圖片資源優(yōu)化侥猬。
- 使用WebP格式例驹。
- 圖片裁剪,壓縮退唠,雪碧圖
- 圖片懶加載
4.3 提升渲染性能
概念:當(dāng)調(diào)用 wx.navigateTo 打開一個(gè)新頁面時(shí)鹃锈,小程序框架會(huì)完成以下幾步:
- 準(zhǔn)備新的 webview 線程環(huán)境,包括基礎(chǔ)庫的初始化瞧预。
- 從邏輯層到視圖層的初始數(shù)據(jù)通信屎债。
- 視圖層根據(jù)邏輯層的數(shù)據(jù),結(jié)合 WXML 片段構(gòu)建出節(jié)點(diǎn)樹(包括節(jié)點(diǎn)屬性垢油、事件綁定等信息)盆驹,最終與 WXSS 結(jié)合完成頁面渲染。
小程序的渲染損耗主要在數(shù)據(jù)通信和節(jié)點(diǎn)樹創(chuàng)建及更新的流程中滩愁,提升渲染性能優(yōu)化的方向:
- 降低線程間通信頻次躯喇。
- 減少線程間通信的數(shù)據(jù)量。
- 減少 WXML 節(jié)點(diǎn)數(shù)量硝枉。
方案1:合并setData調(diào)用廉丽。
盡可能地把多次setData調(diào)用合并成一次。
只把與界面渲染相關(guān)的數(shù)據(jù)放在Data中妻味。
方案2:去掉不必要的事件綁定正压。
不必要的click、touch责球、onPageScroll不要被觸發(fā)焦履。
方案3:去掉不必要的節(jié)點(diǎn)屬性拓劝。
組件節(jié)點(diǎn)支持附加自定義數(shù)據(jù) dataset,當(dāng)用戶事件被觸發(fā)時(shí)嘉裤,視圖層會(huì)把事件 target 和 dataset 數(shù)據(jù)傳輸給邏輯層凿将。如下例:
<view class="tabbar-item" wx:for="{{list}}" wx:key="index" item="item">
<view @tap="tabFn" data-index="{{item}}">
</view>
</view>
methods = {
tabFn (e) {
const item = e.currentTarget.dataset['item'];
console.log(item);
}
};
自定義數(shù)據(jù)量越大,事件通信的耗時(shí)就會(huì)越長价脾,所以盡量減少自定義數(shù)據(jù)量牧抵,或者不用自定義數(shù)據(jù)。
4.4 解決小程序內(nèi)存占用過高的問題
當(dāng)小程序占用系統(tǒng)資源過高侨把,就有可能會(huì)被系統(tǒng)銷毀或被微信客戶端主動(dòng)回收犀变,導(dǎo)致小程序掛掉。
方案1:回收頁面的setTimeout和setInterval計(jì)時(shí)器秋柄。
小程序每一個(gè)頁面都會(huì)獨(dú)立一個(gè) webview 線程获枝,但邏輯層是單線程的,也就是所有的 webview 線程共享一個(gè) JS 線程骇笔,以至于跳轉(zhuǎn)頁面省店,計(jì)時(shí)器還在跑。
onHide或者onUnload的時(shí)候清除計(jì)時(shí)器笨触。
方案2:避免頻發(fā)事件中的重度內(nèi)存操作懦傍。
- onPageScroll 事件回調(diào)使用節(jié)流。
- 避免 CPU 密集型操作芦劣,譬如復(fù)雜的計(jì)算粗俱。
- 避免調(diào)用 setData,或減小 setData 的數(shù)據(jù)量虚吟。
五寸认、優(yōu)化后的結(jié)果
5.1 看下audis下的得分
優(yōu)化之前得分如下:
優(yōu)化之后得分如下:
由此可見,按照以上步驟做小程序性能優(yōu)化之后串慰,audis的綜合評(píng)分是有一個(gè)明顯的進(jìn)步的偏塞。
5.2 優(yōu)化之后,“小程序數(shù)據(jù)助手”中的性能數(shù)據(jù)
六邦鲫、總結(jié)
小程序性能優(yōu)化和H5優(yōu)化一樣灸叼,是一個(gè)根據(jù)多樣性用戶場(chǎng)景做持續(xù)迭代的過程,也是我們?nèi)粘W鰓eb開發(fā)揮之不去的原則和主題掂碱。本文探討了小程序優(yōu)化的各種場(chǎng)景和方案怜姿,希望在以后的項(xiàng)目開發(fā)過程中慎冤,能夠持續(xù)優(yōu)化疼燥,打造出更好的產(chǎn)品。
作者:vivo-Fu Weilang