<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          騰訊課堂小程序性能極致優(yōu)化——綜合篇

          共 7987字,需瀏覽 16分鐘

           ·

          2021-07-20 16:03

          導(dǎo)語(yǔ) | 如果你的小程序也遇到了性能問(wèn)題,我們的實(shí)踐經(jīng)驗(yàn)也許可以給到你啟發(fā),我們從小程序的啟動(dòng)、加載到交互都進(jìn)行了探索。順便說(shuō)一句,這篇文章在騰訊內(nèi)部曾被小程序技術(shù)總監(jiān)打賞。

          1. 緣起

          事情,要從一個(gè)周末愜意的下午開(kāi)始說(shuō)起……

          那天,手機(jī)突然被喚醒,彈出多條微信消息。原來(lái)是這周末正在校園推廣的活動(dòng)群發(fā)來(lái)的,想起之前大家有條不紊的開(kāi)發(fā)進(jìn)度,和產(chǎn)品溝通的友好過(guò)程,應(yīng)該是活動(dòng)反響不錯(cuò)。

          現(xiàn)實(shí)是殘酷的:

          “我們的小程序打開(kāi)慢成狗!”

          “這個(gè) loading 加載的過(guò)程也太久了!”

          “滾動(dòng)加載有點(diǎn)卡,而且很容易報(bào)錯(cuò)……”

          看到的是最直接的控訴。

          看到用戶的錄屏,這幾個(gè)問(wèn)題確實(shí)是有出現(xiàn),所以我們還是需要對(duì)小程序進(jìn)行一次主流程的性能優(yōu)化,三句控訴可以總結(jié)為3個(gè)點(diǎn):

          • 小程序啟動(dòng)慢

          • 小程序請(qǐng)求慢

          • 小程序交互慢

          2.定位

          2.1. 啟動(dòng)慢

          收到反饋后第一反應(yīng)是,用戶是不是網(wǎng)速太慢了,自己跑一遍,發(fā)現(xiàn)自己的手機(jī)跑起來(lái)都是沒(méi)問(wèn)題的,灰常流暢,下意識(shí)的可能想錄個(gè)屏回復(fù)過(guò)去。

          不過(guò)有用戶錄屏在那,當(dāng)然不能這么草率,所以我們查了下管理后臺(tái)小程序在不同網(wǎng)絡(luò)下的大盤(pán)數(shù)據(jù):

          網(wǎng)絡(luò)啟動(dòng)耗時(shí)
          總體3.6s
          WIFI3.5s
          4G3.9s
          2G/3G4.1s


          從統(tǒng)計(jì)看,總體3.7s的啟動(dòng)耗時(shí),網(wǎng)絡(luò)對(duì)于啟動(dòng)耗時(shí)是會(huì)有影響的,但影響沒(méi)有很大,就算是2G-3G下跟大盤(pán)的數(shù)據(jù)對(duì)比也沒(méi)有慢很多,可見(jiàn)事情并不簡(jiǎn)單。

          于是我們從另外一個(gè)維度來(lái)看一下大盤(pán)數(shù)據(jù):

          機(jī)型啟動(dòng)耗時(shí)JS注入初次渲染
          總體3.6s0.29s 0.16s
          高端機(jī)2.9s0.19s0.06s
          中端機(jī)4.8s0.42s0.19s
          低端機(jī)7.9s0.72s0.43s

          從這里就可以看出問(wèn)題來(lái)了,手機(jī)的性能對(duì)于小程序的啟動(dòng)速度影響非常大,低端機(jī)相對(duì)高端機(jī)有2-3倍的差距,特別是渲染層甚至有5-6倍的差距,而且問(wèn)題反饋的用戶所使用的手機(jī)也確實(shí)是中低端機(jī),但用戶使用什么手機(jī)我們也沒(méi)法控制,那這里有辦法去優(yōu)化嗎?

          針對(duì)這個(gè)問(wèn)題,我們需要了解一下小程序的啟動(dòng)過(guò)程,根據(jù)官方文檔的介紹,小程序的啟動(dòng)可以分為下面幾個(gè)步驟:

          上圖描述了用戶點(diǎn)擊小程序開(kāi)始到頁(yè)面開(kāi)始請(qǐng)求數(shù)據(jù)的一個(gè)完整的冷啟動(dòng)過(guò)程,而小程序初始化的過(guò)程(信息準(zhǔn)備、環(huán)境準(zhǔn)備)占用了比較長(zhǎng)的時(shí)間,但這部分的工作是由微信客戶端來(lái)完成,開(kāi)發(fā)者無(wú)法干預(yù),所以我們只能聚焦于后續(xù)的步驟(下載代碼包、注入代碼包、初次渲染)。

          根據(jù)官方文檔的介紹,這一部分的可優(yōu)化手段有:

          1. 減小代碼包體積

          2. 降低代碼復(fù)雜度

          3. 減少同步代碼接口調(diào)用

          4. 降低頁(yè)面結(jié)構(gòu)復(fù)雜度

          5. 減少自定義組件數(shù)量

          后面4條在技術(shù)上沒(méi)有特別好的限制手段,需要我們?cè)?Code Review 的時(shí)候?qū)?fù)雜度和開(kāi)銷(xiāo)大的接口調(diào)用進(jìn)行把關(guān),復(fù)雜度這里還可以借助如 CodeCC(騰訊內(nèi)部代碼檢查工具) 這類(lèi)工具去進(jìn)行分析,減少自定義組件數(shù)量,這個(gè)是比較難以抉擇的,需要在代碼可讀性、可復(fù)用性之間做個(gè)取舍,不是本次優(yōu)化的重點(diǎn)。

          所以我們就重點(diǎn)關(guān)注的代碼包體積問(wèn)題,通過(guò)我們的 CI 記錄可以收集到我們的總包大?。?/span>

          可以看到主包體積達(dá)到1949.71KB,接近了2M的極限了,在通過(guò)依賴分析后,發(fā)現(xiàn)除了一些是未使用到的模塊和組件外,很大一部分內(nèi)容是靜態(tài)資源,同時(shí)我們也在官方文檔中看到這樣一句話:

          小程序代碼包在下載時(shí)會(huì)使用 ZSTD 算法進(jìn)行壓縮,這些資源文件會(huì)占用大量代碼包體積,并且通常難以進(jìn)一步被壓縮,對(duì)于下載耗時(shí)的影響比代碼文件大得多。

          所以我們要減小代碼包的體積,最直接的方法就是將非必要的資源去除掉:

          • 對(duì)靜態(tài)資源進(jìn)行優(yōu)化,將非必要的靜態(tài)資源文件上傳到CDN

          • 對(duì)小程序的組件進(jìn)行依賴分析,過(guò)濾掉未使用的組件

          同時(shí)我們還關(guān)注到,有一些分包特別小,但是由于是普通分包,在打開(kāi)這些頁(yè)面的時(shí)候還需要先下載主包,這里在包下載耗時(shí)上其實(shí)是有一些浪費(fèi)的,比較典型的就是 WebView 頁(yè)面,他們往往只需要對(duì)參數(shù)進(jìn)行處理,對(duì)于主包的依賴不是很強(qiáng),所以這里還有一個(gè)可以優(yōu)化的點(diǎn):

          • 對(duì)獨(dú)立性比較強(qiáng)的頁(yè)面進(jìn)行獨(dú)立分包,盡可能的減少包下載耗時(shí)

          2.2. 請(qǐng)求慢

          我們通過(guò)日志查到這個(gè)用戶的首頁(yè)數(shù)據(jù)請(qǐng)求返回會(huì)到3-4s,請(qǐng)求慢在正常情況下會(huì)有這么兩種情況:

          • 并發(fā)量突增導(dǎo)致服務(wù)器響應(yīng)慢

          • 用戶網(wǎng)速較慢導(dǎo)致發(fā)送請(qǐng)求和接收請(qǐng)求變慢

          我們通過(guò)日志統(tǒng)計(jì)發(fā)現(xiàn)用戶的訪問(wèn)時(shí)間端,請(qǐng)求量跟平時(shí)保持一致,看大盤(pán)請(qǐng)求耗時(shí)的統(tǒng)計(jì),也沒(méi)有產(chǎn)生大的波動(dòng):

          所以基本可以排除是后臺(tái)的問(wèn)題,雖然大盤(pán)的數(shù)據(jù)在500ms左右,但是當(dāng)用戶網(wǎng)絡(luò)不好的情況下,這一塊要怎么保證呢?

          答案當(dāng)然是做提前拉取,當(dāng)用戶冷啟動(dòng)的時(shí)候,我們可以使用小程序官方提供的數(shù)據(jù)預(yù)拉取能力提前拉取,從小程序的啟動(dòng)耗時(shí)看,完全可以 cover 掉我們的接口請(qǐng)求耗時(shí),可以讓小程序啟動(dòng)成功后就直接渲染頁(yè)面。

          在熱啟動(dòng)的情況下,請(qǐng)求慢主要體現(xiàn)在用戶交互時(shí)發(fā)生的請(qǐng)求和頁(yè)面切換時(shí)發(fā)生的請(qǐng)求,交互的情況我們下一節(jié)在分析,這里主要看頁(yè)面切換,從我們的統(tǒng)計(jì)數(shù)據(jù)來(lái)看,頁(yè)面切換的耗時(shí)大概在400ms左右,而其中能夠利用的時(shí)間大概是50ms-100ms


          route時(shí)間


          利用頁(yè)面切換的這個(gè)時(shí)間提前對(duì)頁(yè)面的數(shù)據(jù)進(jìn)行加載,可以減少用戶感觀上的數(shù)據(jù)請(qǐng)求時(shí)間,同時(shí)在第一次請(qǐng)求之后可以根據(jù)一定的策略對(duì)頁(yè)面的數(shù)據(jù)進(jìn)行緩存,從而可以達(dá)到二次進(jìn)入頁(yè)面秒開(kāi)的效果。

          總結(jié)來(lái)看,請(qǐng)求慢的優(yōu)化手段有下面幾個(gè),而且理論上效果都會(huì)很顯著:

          • 冷啟動(dòng)開(kāi)啟數(shù)據(jù)預(yù)拉取

          • 頁(yè)面路由切換時(shí)提前拉取數(shù)據(jù)

          • 對(duì)數(shù)據(jù)進(jìn)行緩存

          2.3. 交互慢

          先說(shuō)一下這里的交互慢具體指什么,我們收到用戶反饋的現(xiàn)象是:用戶首屏順利加載出來(lái)之后,后續(xù)滾動(dòng)加載和一些按鈕點(diǎn)擊的響應(yīng)非常慢并且很容易報(bào)錯(cuò)。收到這個(gè)反饋后定位了很久,講道理如果是因?yàn)橛脩艟W(wǎng)絡(luò)問(wèn)題導(dǎo)致的請(qǐng)求很慢,應(yīng)該所有的請(qǐng)求都會(huì)很慢,但是用戶表現(xiàn)出來(lái)的現(xiàn)象是后續(xù)的加載以及交互很慢,反而首屏還算正常。

          通過(guò)日志查詢,我們發(fā)現(xiàn)這個(gè)用戶的請(qǐng)求報(bào)錯(cuò)都是請(qǐng)求超時(shí),為什么超時(shí)會(huì)集中在交互加載這里呢?定位了一段時(shí)間后我們發(fā)現(xiàn)一個(gè)用戶的報(bào)錯(cuò)都集中在首屏加載之后就立馬下滑或者點(diǎn)擊,如果過(guò)了一段時(shí)間再點(diǎn)擊又不會(huì)報(bào)錯(cuò)。

          發(fā)現(xiàn)這個(gè)現(xiàn)象后,我們想到了官方文檔關(guān)于網(wǎng)絡(luò)使用說(shuō)明的一個(gè)限制:

          wx.request、wx.uploadFile、wx.downloadFile的最大并發(fā)限制是 10 個(gè)

          再結(jié)合我們對(duì)于 wx.request 的封裝,請(qǐng)求超時(shí)的計(jì)時(shí)器是從調(diào)用 wx.request 的時(shí)候就開(kāi)始了,如果請(qǐng)求并發(fā)超過(guò)了限制,那么就很容易出現(xiàn)請(qǐng)求超時(shí),而當(dāng)我們從第一個(gè)業(yè)務(wù)接口請(qǐng)求到數(shù)據(jù)后就會(huì)進(jìn)行一系列的數(shù)據(jù)上報(bào),包括 pv、組件曝光、關(guān)鍵鏈路打點(diǎn)等等,所以我們利用 Whistle 的 resDelay 方法,將我們的上報(bào)請(qǐng)求都延遲5000ms返回,果然就復(fù)現(xiàn)了用戶反饋的那種情況。

          找到問(wèn)題之后也就明確了需要優(yōu)化的方向:

          • 保障與用戶體驗(yàn)相關(guān)的業(yè)務(wù)請(qǐng)求正常發(fā)送

          交互慢還有別的原因嗎?在繼續(xù)挖掘性能瓶頸的過(guò)程中,發(fā)現(xiàn)騰訊課堂小程序的課程詳情頁(yè)內(nèi)容非常多,有5-6屏的高度,而用戶只會(huì)關(guān)心首屏是不是更快的呈現(xiàn)出來(lái),但是我們?cè)镜奶幚矸绞绞潜容^粗暴的,拿到詳情頁(yè)的數(shù)據(jù)之后對(duì)數(shù)據(jù)進(jìn)行處理,格式化成整個(gè)頁(yè)面所需要的數(shù)據(jù)之后一次性調(diào)用 this.setData 來(lái)更新頁(yè)面,所以如果要提升首屏速度,這里需要做的就是:

          • 頁(yè)面分步渲染

          2.4. 優(yōu)化點(diǎn)歸總

          再歸總一下需要優(yōu)化的點(diǎn)和方向:

          1. 啟動(dòng)慢主要從優(yōu)化代碼包上下手:

            • 對(duì)靜態(tài)資源進(jìn)行優(yōu)化,將非必要的靜態(tài)資源文件上傳到CDN

            • 對(duì)小程序的組件進(jìn)行依賴分析,過(guò)濾掉未使用的組件

            • 對(duì)獨(dú)立性比較強(qiáng)的頁(yè)面進(jìn)行獨(dú)立分包,減少主包下載耗時(shí)

          2. 請(qǐng)求慢主要從預(yù)加載和緩存下手:

            • 冷啟動(dòng)開(kāi)啟數(shù)據(jù)預(yù)拉取

            • 頁(yè)面路由切換時(shí)提前拉取數(shù)據(jù)

            • 對(duì)數(shù)據(jù)進(jìn)行緩存

          3. 交互慢需要從發(fā)起請(qǐng)求和頁(yè)面渲染下手:

            • 保障與用戶體驗(yàn)相關(guān)的業(yè)務(wù)請(qǐng)求正常發(fā)送

            • 頁(yè)面分步渲染

          3. 優(yōu)化

          3.1. 啟動(dòng)優(yōu)化

          3.1.1. 獨(dú)立分包

          由于用戶反饋主要是因?yàn)樾@推廣活動(dòng)來(lái)的,而活動(dòng)頁(yè)面我們是通過(guò) WebView 內(nèi)嵌 H5 來(lái)承載的,而 WebView 頁(yè)面的啟動(dòng)過(guò)程和小程序原生頁(yè)面還不太一樣:



          實(shí)際上 WebView 頁(yè)面只需要完善登錄態(tài)傳遞的功能,對(duì)于主包的依賴不是很大,而且這部分頁(yè)面更大的性能問(wèn)題需要在 h5 那邊來(lái)優(yōu)化,所以我們第一時(shí)間對(duì)其進(jìn)行了獨(dú)立分包處理。

          最終的優(yōu)化效果還不錯(cuò),因?yàn)閱?dòng)過(guò)程中不需要下載主包,啟動(dòng)性能提升了30%。

          3.1.2. 靜態(tài)資源上CDN

          我們小程序構(gòu)成主要是由原生頁(yè)面 + kbone 頁(yè)面組成的,kbone 是采用的官方的方案,通過(guò) webpack 構(gòu)建,有很多單獨(dú)打包靜態(tài)資源的方案。而我們的原生頁(yè)面是使用 gulp 進(jìn)行構(gòu)建的,原來(lái)主要的功能是將源碼中的 ts 轉(zhuǎn)成 js,同時(shí)對(duì) css 文件通過(guò) postcss 轉(zhuǎn)成 wxss,由于 wxss 不支持引用相對(duì)路徑,所以在 wxss 中引用的圖片和字體都是轉(zhuǎn)成 base64 的,然后對(duì)其余的文件如 json、wxml 文件則直接復(fù)制到產(chǎn)物中去。

          這樣的處理方式比較粗暴,通過(guò) postcss 將 background-image 所引用的本地圖片都轉(zhuǎn)成 base64,還會(huì)導(dǎo)致很多圖片在項(xiàng)目中占用了2倍的體積。


          CI流程-優(yōu)化前


          所以我們首先需要將源碼下的靜態(tài)資源匹配到并單獨(dú)構(gòu)建出來(lái),并且為了規(guī)避同名文件的問(wèn)題,需要對(duì)資源打個(gè) hashtag,我們這里需要用到一個(gè) gulp 插件gulp-rev,這個(gè)插件可以對(duì)基于資源的內(nèi)容進(jìn)行 hash。


          CI流程-優(yōu)化后


          將圖片上到 CDN 后,把 css、js、json、wxml 中的引用路徑替換成 CDN 地址,具體的替換邏輯如下。


          CDN流程


          3.1.3. 過(guò)濾未使用組件

          隨著業(yè)務(wù)的迭代,不可避免的會(huì)有一些組件被廢棄了但是難以察覺(jué),通過(guò)我們團(tuán)隊(duì)開(kāi)發(fā)的小程序腳手架 imweb-miniprogram-cli 對(duì)頁(yè)面所使用到的組件進(jìn)行分析,可以把項(xiàng)目中未使用到的組件給過(guò)濾出來(lái),不會(huì)打包到最終的產(chǎn)物中,大致的思路如下:


          組件依賴


          app.json開(kāi)始,拿到小程序所配置的所有頁(yè)面和分包,通過(guò)檢查 App、頁(yè)面、分包中所使用的自定義組件來(lái)進(jìn)行收集,并且遞歸檢查自定義組件所使用的組件,如果檢測(cè)到有未使用的組件,也會(huì)給到提示,非常友好:


          組件過(guò)濾


          可以看到在我們的項(xiàng)目中就發(fā)現(xiàn)了好幾個(gè)未使用到的組件。

          3.2. 請(qǐng)求優(yōu)化

          3.2.1. 數(shù)據(jù)預(yù)拉取

          數(shù)據(jù)預(yù)拉取需要在小程序的管理后臺(tái)開(kāi)啟,數(shù)據(jù)來(lái)源可以選擇開(kāi)發(fā)者服務(wù)器或者云開(kāi)發(fā),選擇開(kāi)發(fā)者服務(wù)器的話會(huì)有一些限制,如果是直接填寫(xiě) CGI 地址,就只能拉取一種數(shù)據(jù),不夠靈活,而如果再搭建一個(gè)服務(wù)去做預(yù)拉取涉及到的工作量又會(huì)很大,所以我們選擇的是云開(kāi)發(fā)的方式,大致流程如下圖:


          數(shù)據(jù)預(yù)拉取-大概


          當(dāng)小程序啟動(dòng)的時(shí)候,微信客戶端會(huì)根據(jù)配置去拉取指定的云函數(shù),在云函數(shù)中通過(guò) cl5 調(diào)用業(yè)務(wù)后臺(tái)的服務(wù)拉取到需要的數(shù)據(jù),拉取到后客戶端會(huì)將數(shù)據(jù)緩存在本地,當(dāng)小程序啟動(dòng)成功后,在業(yè)務(wù)代碼中調(diào)用wx.getBackgroundFetchData就可以拿到預(yù)拉取的數(shù)據(jù),如果緩存數(shù)據(jù)拉到的是所需要的數(shù)據(jù)則可以直接渲染,如果不是則降級(jí)到業(yè)務(wù)中再拉一次接口。

          在云函數(shù)中可以拿到本次小程序啟動(dòng)的pathquery參數(shù),所以我們可以根據(jù)這兩個(gè)參數(shù)來(lái)判斷本次預(yù)拉取需要調(diào)用業(yè)務(wù)后臺(tái)的哪個(gè)服務(wù),從而達(dá)到從不同的頁(yè)面啟動(dòng)小程序都可以通過(guò)一個(gè)云函數(shù)預(yù)拉取到所需要的數(shù)據(jù)。

          const preFetchMap = {
            'pages/index/index': fetchIndex,
            'pages/course/course': fetchCourse,
          }

          // 云函數(shù)入口函數(shù)
          exports.main = async (event) => {
            const { path, query = '' } = event;
            const fetchFn = preFetchMap[path];

            if (fetchFn) {
              const res = await fetchFn(query);
              return res;
            }

            return {
              error: {
                event,
                retcode-1002,
                msg`${path}頁(yè)面未設(shè)置預(yù)拉取邏輯`
              }
            };
          };

          不過(guò)要注意的是,因?yàn)樾〕绦蜃陨碜隽撕芏喑跏蓟膬?yōu)化,有可能在小程序啟動(dòng)后,預(yù)拉取的數(shù)據(jù)還沒(méi)有返回,所以我們做了進(jìn)一步的優(yōu)化,在業(yè)務(wù)拉取的過(guò)程中通過(guò) wx.onBackgroundFetchData監(jiān)聽(tīng)預(yù)拉取的返回,收到返回就直接渲染 ,盡可能的使用預(yù)拉取的數(shù)據(jù)來(lái)渲染首屏。


          數(shù)據(jù)預(yù)拉取


          3.2.2. 提前拉取 & 數(shù)據(jù)緩存

          前面已經(jīng)提到過(guò),提前拉取就是要利用小程序切換頁(yè)面的空隙開(kāi)始拉取數(shù)據(jù),從而在感官上較少數(shù)據(jù)請(qǐng)求的時(shí)間,整體的邏輯是通過(guò)封裝的跳轉(zhuǎn)邏輯,對(duì)應(yīng)的頁(yè)面添加不同的數(shù)據(jù)拉取邏輯,并將拉取的 promise 掛載在 App 上,當(dāng)頁(yè)面切換完成后優(yōu)先使用 App 上的 promise 來(lái)獲取數(shù)據(jù)。

          數(shù)據(jù)緩存則是在數(shù)據(jù)拉取成功后,將比較固定的數(shù)據(jù)通過(guò) wx.setStorage 緩存在本地,當(dāng)?shù)诙吻袚Q到這個(gè)頁(yè)面時(shí),先使用本地緩存的數(shù)據(jù)進(jìn)行渲染,后面再通過(guò)拉取的數(shù)據(jù)來(lái)進(jìn)行更新。


          提前拉取


          3.3. 交互優(yōu)化

          3.3.1. 業(yè)務(wù)請(qǐng)求保障

          保障業(yè)務(wù)請(qǐng)求的核心思路是讓業(yè)務(wù)請(qǐng)求優(yōu)先,我們封裝了一個(gè) 排隊(duì)請(qǐng)求模塊 ,通過(guò)對(duì) wx.request API的攔截,將請(qǐng)求根據(jù)配置有個(gè)優(yōu)先級(jí)排序,低優(yōu)先級(jí)的會(huì)在請(qǐng)求并發(fā)數(shù)達(dá)到一定的閾值之后被推到等待隊(duì)列 WaitingQueue 中,留出足夠的通道給到高優(yōu)先級(jí)的業(yè)務(wù)請(qǐng)求。


          請(qǐng)求排隊(duì)


          3.3.2. 分步渲染

          這里的方案相信大家也能很好理解,主要是優(yōu)先處理首屏需要的數(shù)據(jù)并通過(guò) setData 更新視圖,然后在處理其余的數(shù)據(jù)。但根據(jù)官方文檔的說(shuō)明:

          setData 函數(shù)用于將數(shù)據(jù)從邏輯層發(fā)送到視圖層(異步),同時(shí)改變對(duì)應(yīng)的 this.data 的值(同步)。

          而小程序代碼執(zhí)行順序也遵循JS的事件循環(huán)機(jī)制,僅僅是在處理后的數(shù)據(jù)調(diào)用 setData ,然后繼續(xù)或者通過(guò) Promise 處理下一步的話,并不能達(dá)到分步渲染的目的,而直接通過(guò)回調(diào)的方式在 setTimeout 中使用嵌套渲染,代碼的可讀性會(huì)變差,同時(shí)也不是很優(yōu)雅。我們的解決方式是利用 setTimeout 封裝了一個(gè)符合 Promise 標(biāo)準(zhǔn)的方法,從而可以像使用 Promise 那樣繼續(xù)分步渲染:



          4. 成果

          經(jīng)過(guò)這一系列的優(yōu)化,效果還是比較明顯的:

          4.1. 包大小

          在包大小方面:

          • 總包從9132.94KB減小到6736.42KB,減少了27%;

          • 主包從1949.71KB減小到985.96KB,減少了49.5%;

          從啟動(dòng)耗時(shí)的數(shù)據(jù)看,下載耗時(shí)和JS注入耗時(shí)都有明顯的下降:


          啟動(dòng)耗時(shí)


          再看打開(kāi)耗時(shí)分布,可以看到3s內(nèi)打開(kāi)的用戶比例有明顯增加,從56.26%增加到64.25%;


          打開(kāi)分布


          4.2. 請(qǐng)求耗時(shí)

          數(shù)據(jù)預(yù)拉取,提前拉取,數(shù)據(jù)緩存在冷啟動(dòng)和頁(yè)面切換時(shí)都起到了很不錯(cuò)的效果:

          首頁(yè)請(qǐng)求速度從平均400ms下降到50ms,優(yōu)化了87.5%

          課詳頁(yè)的請(qǐng)求速度從平均800ms下降到90ms,優(yōu)化了88.75%;

          而數(shù)據(jù)緩存讓二次訪問(wèn)時(shí)頁(yè)面可以秒開(kāi):


          二次加載


          使用排隊(duì)請(qǐng)求之后,對(duì)網(wǎng)絡(luò)請(qǐng)求順序的干預(yù)效果還比較明顯,灰度用戶業(yè)務(wù)請(qǐng)求耗時(shí)平均有50-100ms,約15%的優(yōu)化;

          同時(shí)我們通過(guò)分析耗時(shí)分別在80分位、50分位、20分位的效果發(fā)現(xiàn),請(qǐng)求耗時(shí)越長(zhǎng),優(yōu)化效果越明顯,也就是說(shuō)在弱網(wǎng)情況下能夠更好的發(fā)揮作用。


          請(qǐng)求排隊(duì)結(jié)果


          4.3. 渲染

          使用分步渲染后,我們的頁(yè)面可以在處理完首屏的基礎(chǔ)數(shù)據(jù)之后就立即開(kāi)始渲染了,由于我們的目錄結(jié)構(gòu)比較復(fù)雜,處理起來(lái)耗時(shí)比較長(zhǎng),所以第二部才處理目錄,實(shí)際的渲染效果如下圖:


          分步渲染


          首屏可以比原來(lái)提前100ms-150ms渲染出來(lái)。

          5. 總結(jié)

          我們本次的性能優(yōu)化對(duì)小程序啟動(dòng)、請(qǐng)求、交互、渲染多個(gè)方面都進(jìn)行了性能的挖掘,是在對(duì)基礎(chǔ)庫(kù)版本要求不高的情況下能做到的極致了。

          以我們的核心頁(yè)面首頁(yè)和課程詳情頁(yè)來(lái)說(shuō):

          • 首頁(yè)冷啟動(dòng)耗時(shí)開(kāi)發(fā)者可干預(yù)的部分優(yōu)化大概是1300下載 + 300注入 + 170首渲 + 430請(qǐng)求 = 2200ms -> 750 + 245 + 170 + 50 = 1215ms,優(yōu)化了45%

          • 頁(yè)冷啟動(dòng)耗時(shí)開(kāi)發(fā)者可干預(yù)的部分優(yōu)化大概是1300下載 + 300注入 + 170首渲 + 790請(qǐng)求 = 2560ms -> 750 + 245 + 170 + 100 = 1265ms,優(yōu)化了50.5%

          • 頁(yè)面切換首次進(jìn)入詳情頁(yè)耗時(shí)從400路由 + 800請(qǐng)求 + 450處理 = 1650ms -> 400 + 720 + 300 = 1420ms,優(yōu)化了14%

          • 二次進(jìn)入詳情頁(yè)面幾乎看不到加載和渲染過(guò)程

          還有更多的優(yōu)化手段嗎?官方還提供了一些比較高級(jí)的功能,對(duì)基礎(chǔ)庫(kù)版本要求比較高的,例如:

          • 組件的按需注入和用時(shí)注入可以進(jìn)一步減小代碼包下載耗時(shí),但是在我們發(fā)布時(shí)這個(gè)功能還有點(diǎn)問(wèn)題,會(huì)導(dǎo)致首頁(yè)的自定義組件加載不出來(lái),所以暫時(shí)沒(méi)有使用到。

          • 還可以使用2.11.1開(kāi)始支持的初始渲染緩存,不必等到邏輯層初始化完畢,可以更早的開(kāi)始渲染視圖。

          • 尚在試驗(yàn)階段的分包異步化,利用異步加載模塊的方式也可以減少代碼包的下載耗時(shí)和JS的注入耗時(shí)。

          利用這些能力可以在更多細(xì)節(jié)上進(jìn)行優(yōu)化,我們也將進(jìn)一步探索和跟進(jìn),如果你有更好的方案歡迎討論。




          這篇文章是騰訊課堂小程序優(yōu)化的綜合介紹,后面我們還有【網(wǎng)絡(luò)請(qǐng)求優(yōu)化】和【獨(dú)立分包與性能測(cè)試】?jī)蓚€(gè)專(zhuān)項(xiàng)優(yōu)化,敬請(qǐng)期待。


          內(nèi)推社群


          我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。


          瀏覽 66
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  翔田千里毛片 | 久久精品2019中文字幕人妻欧 | 爱草视频| 爱爱网免费视频 | 无码国产精品一区二区 |