<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>

          Performance 如何排查性能問(wèn)題

          共 4743字,需瀏覽 10分鐘

           ·

          2021-03-18 09:55

          點(diǎn)上方藍(lán)字關(guān)注公眾號(hào)「前端UpUp

          作者:ES2049

          來(lái)源:https://segmentfault.com/a/1190000038442806

          前言

          Chrome 瀏覽器的 Performance 面板為我們提供了檢測(cè)頁(yè)面性能的能力,但其提供的遠(yuǎn)不止一些性能數(shù)據(jù)。本文將從工作原理的視角,結(jié)合實(shí)際工程的錄制結(jié)果,探一探性能面板向我們透露的其他信息。

          性能面板

          關(guān)于面板的功能與使用方法,可以參考這篇文章。本節(jié)主要介紹瀏覽器架構(gòu)與性能面板的關(guān)系。

          因?yàn)樯形礇Q出最終的標(biāo)準(zhǔn)架構(gòu),各大瀏覽器的實(shí)現(xiàn)細(xì)節(jié)各有不同。這里我們以 Chrome 的架構(gòu)為例,對(duì)照其架構(gòu)與性能面板的關(guān)系。

          由下圖我們可以看到性能面板呈現(xiàn)的幾個(gè)主要線程。性能面板并不包含架構(gòu)中全部的線程,主要還是與頁(yè)面渲染過(guò)程相關(guān)的部分。

          Network

          Network 代表瀏覽器進(jìn)程中的網(wǎng)絡(luò)線程,我們可以看到時(shí)間軸上包含了所有的網(wǎng)絡(luò)請(qǐng)求和文件下載的調(diào)用信息,并以不同顏色標(biāo)識(shí)不同類型的資源。

          Main

          Main 代表渲染進(jìn)程中的主線程,渲染相關(guān)的事情基本都是它來(lái)做,腳本執(zhí)行、樣式計(jì)算、布局計(jì)算、繪制等等。

          Compositor & Raster

          Compositor 代表渲染進(jìn)程中的合成線程,Raster 代表渲染進(jìn)程中的柵格線程。如今瀏覽器繪制一個(gè)頁(yè)面,可以分為以下幾步:

          • 主線程將頁(yè)面分成若干圖層(后文中會(huì)提及 Update Layer Tree)
          • 柵格線程分別對(duì)每一個(gè)層進(jìn)行柵格化處理
          • 合成線程將柵格化的圖塊合并成一個(gè)頁(yè)面


          我們可以看到,在性能面板中主線程在最后調(diào)用了柵格線程做實(shí)際的渲染。

          GPU

          顯然,這部分就是 GPU Process 中的 GPU 線程。

          瀏覽器的工作報(bào)告

          接下來(lái)我們將大致從時(shí)間維度,看看瀏覽器錄制下來(lái)的「工作報(bào)告」。

          文檔的下載解析

          我們旅途的起點(diǎn)將從點(diǎn)擊 Chrome Performance Panel 的 Reload 按鈕(形如刷新)開(kāi)始。當(dāng)前頁(yè)面首先進(jìn)行卸載,伴隨著幾個(gè)日志上報(bào),瀏覽器開(kāi)始了 index.html 的下載工作。

          HTML 文檔下載完成后,瀏覽器開(kāi)始按照 HTML 標(biāo)準(zhǔn)對(duì) index.html 進(jìn)行解析,在主線程中將接收到的文本字符串解析為 DOM 。我們可以注意到,HTML 的解析過(guò)程并不是一氣呵成,這是因?yàn)?HTML 通常還包括了其他外部資源,如圖片、CSS、JS 等。這些文件需要通過(guò)網(wǎng)絡(luò)請(qǐng)求或緩存來(lái)獲取。其中,當(dāng) HTML 解析器解析到 <script> 標(biāo)簽時(shí),HTML 文檔的解析過(guò)程就會(huì)中止,轉(zhuǎn)而去加載、解析和執(zhí)行腳本。因此,從主線程的時(shí)間軸可以看出,Parse HTML 的過(guò)程是斷斷續(xù)續(xù)的。

          不同資源的處理

          以下處理策略都可以在主線程中看到,但是不同資源的處理?xiàng)l長(zhǎng)短差距較大,截圖困難,這里不做呈現(xiàn)。

          那么瀏覽器對(duì)不同資源的處理策略是怎樣的呢?

          • 瀏覽器下載 HTML 并解析,如果遇到外部 CSS 等資源,就會(huì)由 Browser 進(jìn)程中的 network 線程下載
          • 當(dāng) CSS 下載時(shí),HTML 的解析過(guò)程可以繼續(xù)
          • 當(dāng)解析遇到了外部 Script 標(biāo)簽(不包含 async、defer 屬性)時(shí),解析停止,直到腳本下載并執(zhí)行完成

          總的來(lái)說(shuō),瀏覽器對(duì) HTML 的解析過(guò)程不會(huì)被 CSS、IMG 等資源的下載阻塞,但腳本的加載和執(zhí)行會(huì)終止 HTML 的解析。這主要是因?yàn)?JS 可能會(huì)改變 DOM 的結(jié)構(gòu),或者是 JS 動(dòng)態(tài)加載其他 JS 再改變 DOM 等潛在問(wèn)題。

          顯然,盡管瀏覽器可以并發(fā)幾個(gè) network 線程下載資源,但如果僅像上述策略這樣處理,當(dāng)解析到 <script> 時(shí),如果文件較大或者延遲較高,可能會(huì)發(fā)生「腳本獨(dú)占線程而沒(méi)有其他資源在下載」的空窗期(idle network)。因此,pre-loader (或者 preload scanner 等叫法)將會(huì)在主線程之外,掃描余下的標(biāo)簽,充分利用 network 線程下載其他資源。這種機(jī)制可以優(yōu)化 19% 的加載時(shí)長(zhǎng)。

          腳本的解析執(zhí)行

          對(duì)于重業(yè)務(wù)邏輯的復(fù)雜中后臺(tái)應(yīng)用而言,腳本帶來(lái)的性能開(kāi)銷,往往是占主要地位的。我們從下圖的例子就可以看出,去除 beforeunload 之前的卸載,腳本本身的時(shí)間開(kāi)銷占比已過(guò)半。解析 HTML 在其次,至于其他樣式計(jì)算、微任務(wù)、垃圾回收等等,倒不是最痛的地方。當(dāng)然,該例子工程本身重業(yè)務(wù)邏輯,JavaScript 代碼量決定著其高成本。

          有時(shí)我們可以考慮使用 async 或者 defer 屬性來(lái)提高頁(yè)面性能,二者的差異不再贅述。需要專門說(shuō)明的是動(dòng)態(tài)添加腳本的情況。如下面示例代碼所示,腳本被 append 到文檔中后就會(huì)開(kāi)始下載,并且默認(rèn)和 async 具有一樣的行為,即「先加載完的先執(zhí)行」。

          let script = document.createElement('script');
          script.src = "/xxx/a.js";
          document.body.append(script);

          如果專門設(shè)置了 async 屬性,則會(huì)按照 defer 的行為來(lái),即「先加載到的先執(zhí)行」。

          function loadScript(src) {
            let script = document.createElement('script');
            script.src = src;
            script.async = false;
            document.body.append(script);
          }

          // 因?yàn)?nbsp;async = false,所以按順序先執(zhí)行 big;否則(一般會(huì)先)執(zhí)行 small
          loadScript("/xxx/big.js");
          loadScript("/xxx/small.js");

          從下圖中可以看到,調(diào)用棧中執(zhí)行的 appendChild 方法動(dòng)態(tài)添加了 script 腳本,之后很快開(kāi)始了下載動(dòng)作。動(dòng)態(tài)加載的腳本完成下載后,又第一時(shí)間開(kāi)始了腳本執(zhí)行。

          lifecycle 和 paint timing

          下圖展示的是文章中提及的頁(yè)面生命周期流程圖。本節(jié)我們結(jié)合 Performance,對(duì)照該圖進(jìn)行觀察。

          beforeunload

          因?yàn)?Performance 的錄制是在已有頁(yè)面上進(jìn)行 reload,所以記錄的生命周期從頁(yè)面的卸載開(kāi)始。如下圖 Main 所示,beforeunload 事件首先被瀏覽器觸發(fā)。可以注意到,黃色條 Event: beforeunload 是瀏覽器自身觸發(fā)的活動(dòng),我們稱之為根活動(dòng)(Root activities)。

          pagehide

          從下圖中我們可以注意到,為什么事件的觸發(fā)順序和上面的生命周期流程圖不一致,是 pagehide -> visibilitychange -> unload 呢?事實(shí)上,在瀏覽器之前的設(shè)計(jì)中,如果頁(yè)面在卸載階段可視,visibilitychange 就會(huì)在 pagehide 之后觸發(fā),正如下圖截圖中一樣。這就使得頁(yè)面的卸載在不同可視情況下,有著不一致的生命周期與事件順序,給開(kāi)發(fā)者帶來(lái)復(fù)雜性。

          在未來(lái)新版本瀏覽器中,卸載階段的事件順序會(huì)進(jìn)行統(tǒng)一,目前進(jìn)度在這一 issue 下。也正因?yàn)檫@部分的調(diào)整,unload 已經(jīng)不建議在代碼實(shí)現(xiàn)中使用了。

          first paint

          首先區(qū)分下以下兩個(gè)時(shí)間點(diǎn):

          • first paint:指的是首個(gè)像素開(kāi)始繪制到屏幕上的時(shí)機(jī),例如一個(gè)頁(yè)面的背景色
          • first contentful paint:指的是開(kāi)始繪制內(nèi)容的時(shí)機(jī),如文字或圖片
          image.png

          從 Performance 中,我們可以看出首次繪制的一系列動(dòng)作(有些過(guò)程啪的一下很快啊,截圖就省了):

          1. CSS 加載完成
          2. Parse Stylesheet:解析樣式表,構(gòu)建出 CSSOM
          3. Recalculate Style:重新計(jì)算樣式,確定樣式規(guī)則
          4. Layout:根據(jù)計(jì)算結(jié)果進(jìn)行布局,確定元素的大小和位置
          5. Update Layer Tree:更新渲染層樹
          6. Paint:根據(jù) Layer Tree 繪制頁(yè)面(位置、大小、顏色、邊框、陰影等)
          7. Composite Layers:組合層,瀏覽器將圖層合并后輸出到屏幕

          Layout 之后的過(guò)程很快,這里放大些倍數(shù)來(lái)查看:

          DOMContentLoaded

          DOMContentLoaded 表示 HTML 已經(jīng)完全被加載和解析,當(dāng)然樣式表、圖片等資源還不一定已經(jīng)完成加載。從下圖中可以看到,經(jīng)過(guò)多段 HTML 解析后,DCL 之后就沒(méi)有其他的 Parse HTML 了。

          pageshow/load

          因?qū)Ш蕉沟脼g覽器在窗口內(nèi)呈現(xiàn)文檔時(shí),瀏覽器會(huì)在 window 上觸發(fā) pageshow 事件,具體的時(shí)機(jī)可參考這里。不僅如此,當(dāng)頁(yè)面是初次加載時(shí),pageshow 事件會(huì)在 load 事件后觸發(fā)。

          那么回到 Performance 的時(shí)間軸,從下圖我們可以看到,在紅色虛線(標(biāo)志著 load)之后,瀏覽器觸發(fā)了 pageshow 事件,也就是上文提及的根活動(dòng)。

          任務(wù)與性能問(wèn)題

          比較可惜的是,Performance 還無(wú)法清晰的看出 Event Loop。下圖中灰色的 Task 并不是指宏任務(wù),其代表的是「當(dāng)前主線程忙碌,無(wú)法響應(yīng)用戶交互」;Run Microtasks 則確實(shí)是在一次任務(wù)的末尾執(zhí)行的微任務(wù)。當(dāng)我們點(diǎn)開(kāi)調(diào)用棧觀察時(shí),可以看到源碼中的回調(diào)函數(shù)以及對(duì)應(yīng)的源碼位置。

          通過(guò) Task 可以定位性能出現(xiàn)問(wèn)題的地方。RAIL 模型告訴我們需要重點(diǎn)關(guān)注占用 CPU 超出 50ms 的復(fù)雜任務(wù),以提供連貫的交互體驗(yàn)。當(dāng)然,這里更多的是對(duì)交互階段的響應(yīng)的要求,而不一定是對(duì)初始加載階段的要求。

          總結(jié)

          本文從工作原理的視角,結(jié)合實(shí)際工程的錄制結(jié)果,進(jìn)行了一次實(shí)踐對(duì)理論知識(shí)的檢驗(yàn)。Performance 不僅是性能分析工具,還是探究瀏覽器工作原理的小霸王學(xué)習(xí)機(jī)。總的來(lái)說(shuō),瀏覽器的工作是充實(shí)且復(fù)雜的,與我們打工人的摸魚日常形成了對(duì)比,還是需要進(jìn)一步加深學(xué)習(xí)與思考呀。

          參考鏈接

          [1] Measure performance with the RAIL model
          [2] Get Started With Analyzing Runtime Performance
          [3] Inside look at modern web browser
          [4] JavaScript Start-up Performance
          [5] How browsers work
          [6] How the Browser Pre-loader Makes Pages Load Faster

           感謝大家

          1. 關(guān)注「前端UpUp」,分享精選面試熱點(diǎn)文章。

          2. 加我好友,一起討論算法,2021一起UpUp。

          點(diǎn)個(gè)在看支持我吧
          瀏覽 88
          點(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>
                  在线观看高清无码视频 | 俺去草| 精品蜜桃网 | 日本乱伦一区 | 人妻丰满熟妇av无码区蜜桃 |