作者 | Nicholas Ray
譯者 | 核子可樂
策劃 | 丁曉昀
(相關(guān)資料圖)
大家有沒有遇到過響應(yīng)緩慢、卡頓崩潰的垃圾網(wǎng)站?遇上這類性能缺陷,脾氣火爆的朋友往往果斷選擇:
狂點(diǎn)鼠標(biāo); 退出走人,拉低客源轉(zhuǎn)化率; 搜索引擎排名因此下降。三年多來,維基百科的移動(dòng)版網(wǎng)站也深受一段 JavaScript 代碼的戕害。在低端手機(jī)上,這段 JS 代碼的頁(yè)面加載時(shí)間可能超過 600 毫秒,大大影響了用戶的交互體驗(yàn)。
在本文中,我們將一同了解如何通過幾個(gè)簡(jiǎn)單步驟,讓任務(wù)執(zhí)行時(shí)間有效縮短約 50%。
在 JS 執(zhí)行方面,600 毫秒似乎并不是什么無法接受的問題。但我們不妨想象這樣的場(chǎng)景:當(dāng)這段 600 毫秒的 JS 代碼開始執(zhí)行時(shí),用戶恰好想在加載過程中單擊某個(gè)按鈕。因?yàn)樘囟〞r(shí)段內(nèi)瀏覽器的主線程只能處理一個(gè)任務(wù),所以用戶必須等待以下步驟完成后才能獲得操作反饋:
JS 任務(wù)花 600 毫秒執(zhí)行完畢 點(diǎn)擊處理任務(wù)隨后開始執(zhí)行 瀏覽器執(zhí)行必要的渲染步驟,最終更新頁(yè)面內(nèi)容長(zhǎng)任務(wù)可能導(dǎo)致視覺更新延遲,拖慢點(diǎn)擊程序的處理速度
每個(gè)步驟都需要消耗時(shí)間,而任何超過 100 毫秒的響應(yīng)速度都會(huì)給用戶帶來非常明確的延遲感受。正因?yàn)槿绱耍雀鑼⒁磺泻臅r(shí)超過 50 毫秒的任務(wù)定性為“長(zhǎng)任務(wù)”,認(rèn)為其會(huì)影響頁(yè)面對(duì)用戶輸入的響應(yīng)。他們甚至專門為此制定了“總阻塞時(shí)間”(TBT)的指標(biāo)。
這里有兩個(gè)長(zhǎng)任務(wù)(大于 50 毫秒)——分別耗時(shí) 80 毫秒和 100 毫秒
所謂總阻塞時(shí)間,是指瀏覽器主線程上全部長(zhǎng)任務(wù)在首屏內(nèi)容繪制(FCP)和響應(yīng)時(shí)間(TTI)之間的阻塞部分的總和。換言之,“阻塞部分”代表著每個(gè)長(zhǎng)任務(wù)超出 50 毫秒之外的時(shí)間消耗。
計(jì)算以下示例中的總阻塞時(shí)間:
80 毫秒任務(wù)比 50 毫秒基準(zhǔn)多出 30 毫秒,因此帶來了 30 毫秒的總阻塞時(shí)間。 30 毫秒的任務(wù)不構(gòu)成阻塞時(shí)間,因?yàn)槠湫∮?50 毫秒且不屬于長(zhǎng)任務(wù)。 100 毫秒的任務(wù)比 50 毫秒基準(zhǔn)多出 50 毫秒,因此帶來了 50 毫秒的總阻塞時(shí)間。由于總阻塞時(shí)間對(duì)應(yīng)每個(gè)長(zhǎng)任務(wù)超過 50 毫秒部分的總和,所以示例中的最終結(jié)果是 30 毫秒+50 毫秒=80 毫秒。
在常規(guī)移動(dòng)硬件上進(jìn)行測(cè)試時(shí),谷歌建議站點(diǎn)的總阻塞時(shí)間應(yīng)小于 200 毫秒。但維基百科上一項(xiàng)任務(wù)的執(zhí)行就可能超過 600 毫秒——相當(dāng)于總體上限的 3 倍。
我們?cè)撊绾胃倪M(jìn)性能?
要降低這項(xiàng)指標(biāo),我們需要:
在首屏繪制和交互時(shí)間之間,減少主線程上的工作負(fù)載; 在保證工作內(nèi)容不變的情況下,將長(zhǎng)任務(wù)拆分成多個(gè)不超過 50 毫秒的小任務(wù)。本文主要側(cè)重第一種解決思路。
HTML 解析、繪制和垃圾收集都需要在主線程上運(yùn)行,但引發(fā)總阻塞時(shí)間過長(zhǎng)的罪魁禍?zhǔn)兹匀皇?JS 代碼。畢竟有經(jīng)驗(yàn)的前端開發(fā)者都知道,網(wǎng)站降速背后總有 JS 的身影。
在分析維基百科的移動(dòng)站點(diǎn)時(shí),我發(fā)現(xiàn)_enable 方法占用了大部分執(zhí)行時(shí)間。此方法負(fù)責(zé)對(duì)移動(dòng)站點(diǎn)上的部分開展和折疊行為進(jìn)行初始化。配置文件則顯示,在_enable 方法中,對(duì) jQuery .on("click")方法的調(diào)用同樣速度很慢。
這里的.on("click")調(diào)用負(fù)責(zé)向內(nèi)容中的幾乎所有鏈接附加點(diǎn)擊事件偵聽器,這樣如果點(diǎn)擊的鏈接包含哈希片段,相應(yīng)的部分就會(huì)被展開。對(duì)于鏈接較少的短文章,這部分性能影響幾乎可以忽略不計(jì)。但對(duì)于像“美國(guó)”這類的長(zhǎng)詞條,其內(nèi)容可能包含超過 4000 個(gè)鏈接,因此在低端設(shè)備上的執(zhí)行時(shí)間會(huì)超過 200 毫秒。
更糟糕的是,這種設(shè)計(jì)完全沒有必要。偵聽 haschange 事件的下游代碼已經(jīng)調(diào)用了與點(diǎn)擊事件偵聽器相同的方法。除非窗口位置已經(jīng)指向鏈接目的地,否則點(diǎn)擊鏈接會(huì)對(duì) checkHash 方法調(diào)用兩次——一次用于鏈接點(diǎn)擊事件處理程序,另一次用于 hashchange 處理程序。
這種情況下,最好的方法當(dāng)然是直接刪除這個(gè) JS 代碼塊,在幾乎不影響主線程功能的前提下直接省下近 200 毫秒。
在分析過程中,請(qǐng)始終檢查最耗時(shí)的部分,之后看看有沒有能夠優(yōu)化或者刪掉的代碼。
經(jīng)驗(yàn)之談:加快網(wǎng)站速度的首選方法,永遠(yuǎn)是刪除 JS 代碼。
另一項(xiàng)性能審查顯示,initMediaViewer 方法需要約 100 毫秒的執(zhí)行時(shí)間。此方法負(fù)責(zé)將點(diǎn)擊事件偵聽器附加到內(nèi)容中的各個(gè)縮略圖處,這樣點(diǎn)擊縮略圖即可打開媒體查看器:
與步驟 1 中的鏈接示例類似,這種向頁(yè)面上各個(gè)縮略圖附加事件偵聽器的方法不利于性能擴(kuò)展。
維基百科中的詞條可能包含數(shù)千張相關(guān)圖片。在這些多圖頁(yè)面上運(yùn)行這段代碼時(shí),其執(zhí)行時(shí)間可能超過 100 毫秒,必然增加頁(yè)面的總阻塞時(shí)間。下面來看替代方法。
答案就是 事件委托 。
事件委托是一種強(qiáng)大的技術(shù),允許我們將單個(gè)事件偵聽器附加到單一元素,而該元素可以是大量其他元素的共同祖先。對(duì)于可以添加任意數(shù)量元素的用戶生成內(nèi)容,我們往往可以通過事件委托提高其執(zhí)行效率。整個(gè)過程會(huì)用到事件冒泡,具體如下:
將事件偵聽器附加至容器元素。 在事件處理程序中使用 event 參數(shù),檢查 屬性以查看事件源??梢赃x擇使用 (selector) API 來檢查祖先元素。 如果該事件源就是我們所關(guān)注的元素或者其子元素,則進(jìn)行處理。更新后的代碼如下所示:
我們修改了 initMediaViewer 方法,將一個(gè)點(diǎn)擊事件偵聽器附加到了包含所有圖像的單一容器元素上。 在 onClickImage 方法中,我使用 (selector) API 來檢查點(diǎn)擊是否來自縮略圖元素或者其子元素。如果都不是,代碼會(huì)提前返回,因?yàn)檫@里只需要關(guān)注對(duì)縮略圖的點(diǎn)擊。如果是,則代碼將處理該事件。我們分別通過兩輪部署,將步驟 1 和步驟 2 中的優(yōu)化發(fā)布到了生產(chǎn)環(huán)境。
根據(jù)維基百科的綜合性能測(cè)試數(shù)據(jù),在 Moto G(5)實(shí)機(jī)測(cè)試當(dāng)中,首輪部署將總阻塞時(shí)間縮短了約 200 毫秒,第二輪部署進(jìn)一步縮短了約 80 毫秒??傮w而言,這兩個(gè)步驟讓 Moto G(5)等移動(dòng)設(shè)備訪問長(zhǎng)文章時(shí)的總阻塞時(shí)間縮短了約 300 毫秒。
維基百科通過 Moto G(5)在綜合性能測(cè)試中訪問“瑞典”詞條
雖然仍有進(jìn)一步改進(jìn)空間,且查詢?nèi)蝿?wù)仍高于建議的低端設(shè)備延遲上限,但此次優(yōu)化還是取得了顯著成果。為了更大程度縮短總阻塞時(shí)間,后續(xù)可能有必要將任務(wù)拆分成更多小任務(wù)。
此次試驗(yàn)表明,有針對(duì)性的小規(guī)模優(yōu)化有望實(shí)現(xiàn)顯著的性能改進(jìn)。通過刪除或優(yōu)化特定代碼片段,看似微小的更改也會(huì)對(duì)網(wǎng)站的整體性能產(chǎn)生重大影響。換言之,要想在所有設(shè)備上改善響應(yīng)速度和瀏覽體驗(yàn),并不一定要對(duì)代碼庫(kù)開展復(fù)雜且廣泛的修改。有時(shí)候,小小一點(diǎn)調(diào)整就足以引發(fā)性能質(zhì)變。
原文鏈接:
/blog/300ms-faster-reducing-wikipedias-total-blocking-time/
本文轉(zhuǎn)載來源:
/article/rEoBohpb2NMs8Qe0lwuT
關(guān)鍵詞:
質(zhì)檢
推薦