亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

以通俗的方式理解關(guān)鍵渲染路徑

jzman / 2956人閱讀

摘要:縮短的長度能夠有效降低首屏?xí)r間。即便你使用打包工具只引用了一個外部腳本文件,但是如果這個腳本文件的傳輸延遲和執(zhí)行延遲,會導(dǎo)致后面的非關(guān)鍵資源的請求被延遲,雖然這不會減慢的首屏?xí)r間。

以通俗的方式理解關(guān)鍵渲染路徑

我在看了 google 的 Critical Rendering Path (中文)后, 想把 CRP(Critical Rendering Path) 用通俗易懂的方式描述出來。 官方文檔當(dāng)然是描述最為詳盡且可靠的。 文章里的有些圖片是直接引用自官方文檔。 如果存在侵權(quán), 立刻刪除。

1. 什么是 CRP ?

游覽器從開始請求 HTML 文檔, 到首次渲染到屏幕上(首屏), 背后需要做很多的事情, 這一連串事情就是 CRP 。 開發(fā) app 的時候很多優(yōu)化都是和縮短 CRP 有關(guān)的。 縮短 CRP 的長度能夠有效降低首屏?xí)r間。 這也是理解 CRP 最大的好處。

CRP 大概包括兩個部分: 1、 本地加載渲染, 2、 網(wǎng)絡(luò)請求 。

2. 游覽器的渲染原理

從游覽器加載 HTML 文檔到首屏, 中間發(fā)生了什么?

TL;DR

游覽器解析 HTML 形成 DOM 樹。

游覽器解析 CSS 代碼輸出 CSSOM 。

DOM 和 CSSOM 一起構(gòu)造出一顆 Render Tree 。

游覽器對 Render Tree 上的節(jié)點(diǎn)計算精確位置(布局)。

使用 Render Tree 繪制 (Painting) 到屏幕上。

DOM 用來描述文檔結(jié)構(gòu), 因而是樹形結(jié)構(gòu)。 getElementById , querySelector 等 API 就是針對DOM操作的。 CSSOM 用來描繪 DOM 上的節(jié)點(diǎn)的樣式, 所以圖中的 CSSOM 的根節(jié)點(diǎn)是 body , 這是因為 head 標(biāo)簽沒有樣式信息。 樣式的繼承過程就是發(fā)生在構(gòu)造 CSSOM 的階段。 當(dāng)你使用 element.style 訪問元素的樣式的時候, 其實訪問的是CSSOM 上對應(yīng)的節(jié)點(diǎn)。 然后利用 DOM 和 CSSOM 生成 Render Tree , 這中間做了很多事情, 比如說如果檢測到一個元素所對應(yīng)的 CSSOM 節(jié)點(diǎn)存在 display: none , 那這個節(jié)點(diǎn)將不會輸出到 Render Tree 。

接下來要利用 Render Tree 計算每個節(jié)點(diǎn)在視口上具體的位置, 這就是布局。 布局階段輸出 CSS 的 “盒子模型”, 最后利用這些盒子, 繪制到屏幕上 (Painting) , 在這個過程中如果檢測到 visibility: hidden , 游覽器會將其繪制成一個空白(這里的空白是完完全全的空白, 而不是白色。。。)

現(xiàn)在對于 visibility: hiddendisplay: none 的理解應(yīng)該加深了不少, 具體的區(qū)別可以 google 。

這就是游覽器的大概的渲染過程。

3. 深入 CRP

其實討論 CRP 更多的時候是在討 CSS 和 JS 。 因為 CSS 和 JS 會阻塞渲染。 為什么說 CSS 和 JS 會阻塞渲染? 很難想象如果游覽器先呈現(xiàn)一段沒有樣式的頁面,然后突然刷新, 這是一種及其糟糕的體驗。 而 JS 腳本的執(zhí)行會訪問 DOM 和 CSSOM, 為什么 JS 是同步加載, 而不是異步加載呢? 游覽器為什么不像處理樣式文件一樣處理腳本文件呢? 這其實很好理解, 腳本文件一般包含著你應(yīng)用的邏輯, 如果腳本都是異步加載, 那應(yīng)用的邏輯豈不是亂套。。

什么是 CRP 長度?

獲取所有阻塞資源(關(guān)鍵資源)所需的往返次數(shù), 比如說樣式文件, 腳本文件; 圖片不屬于關(guān)鍵資源, 因為圖片不會導(dǎo)致阻塞游覽器渲染。

關(guān)于 CRP 中幾個關(guān)鍵的時間點(diǎn):

domLoading 這是整個 CRP 的開始的時間點(diǎn)。

domInteractive 游覽器剛好構(gòu)建完 DOM 的時間點(diǎn)。

domContentLoaded DOM 構(gòu)建完成, 且沒有任何樣式會阻塞腳本允許的時間點(diǎn)。 意思就是當(dāng)沒有腳本文件執(zhí)行的時候, DOM 構(gòu)建完成時就到達(dá)這個時間點(diǎn), 畢竟沒有腳本需要執(zhí)行怎么會存在阻塞呢? 不過這種情況很少見。 當(dāng)有腳本文件要執(zhí)行的時候, 樣式( CSSOM )會阻塞腳本文件執(zhí)行, 此時要等到樣式全部就緒的時候才會到達(dá)這個時間點(diǎn)。 所以, 當(dāng)存在腳本文件的時候, 一般 domContentLoaded 會往后推移許多。

domCompelete 表示所有資源都已經(jīng)下載完成, 包括圖片, 字體等。 這個時間點(diǎn)將觸發(fā) onload 事件。

如圖:

talk is cheap show me the code

HTML 文件:




  
  
  
  
  Test


  
emma
Emma.Watson

這段代碼首先在 head 里引用了樣式文件, 在文檔的最后引用了 script 文件, 這是因為 script 標(biāo)簽會導(dǎo)致游覽器阻塞 DOM 的解析, 游覽器甚至?xí)〞r間等待 script 引用的腳本資源的請求過程(同步加載), 直至請求響應(yīng)后且解析完腳本后才會將控制權(quán)交還給游覽器來繼續(xù)解析 DOM 。 因此腳本執(zhí)行時, 很可能 DOM 沒有創(chuàng)建完成, 將 script 放到最后是明智之舉。 這里一共請求了三個資源, 但是幾乎都是同時發(fā)起的, 因此算作一次往返, 所以 CRP 長度是1。

使用 DevTools 的 Timeline 查看頁面加載情況。 如果你對 DevTools 不熟悉, 可以看下 Chrome DevTools 快速入門 的官網(wǎng)文檔 。 實際上這里并用不到很多這方面的知識。 稍微有所了解就行。 在使用 Timeline 的時候要注意開啟隱身模式, 不然會存在諸如游覽器插件等干擾因素。

上面的代碼的加載性能截圖:

可以看到圖中有兩條非常接近的垂直的線, 藍(lán)色表示觸發(fā) DOMContentLoaded 事件的時間點(diǎn), 紅色表示觸發(fā) onload 事件的時間點(diǎn)。兩個時間點(diǎn)非常接近, 這是由于游覽器在請求外部腳本時會等待其響應(yīng), 因此將 DOMContentLoaded 事件向后推遲了。 這里的 CRP 長度是1。 假如沒有腳本文件的請求, 那我們應(yīng)用的 DOMContentLoaded 將在 DOM 解析完成后發(fā)生, 而不至于等到所有樣式就緒( CSS 解析完成)。 這里即便是將腳本文件內(nèi)聯(lián)到 HTML 文檔中也是一樣的結(jié)果。 因為腳本代碼的執(zhí)行會訪問 CSSOM , 游覽器在解析腳本代碼的時候會等待所有 CSSOM 就緒才開始執(zhí)行腳本代碼。 因此效果是一樣的。

這是上面代碼的 CRP 流程圖:

可以看到在 T1 到 T2 這個時間段, 游覽器解析 DOM 的操作是被阻塞的。 即便內(nèi)聯(lián)了腳本文件。

因為大多數(shù)的 JavaScript 框架的邏輯開始部分都是從 DOMContentLoaded 事件點(diǎn)開始的(因為 DOMContentLoaded 總比 onload 快, onload 會等到圖片等資源都就緒才觸發(fā), 會拖慢首屏?xí)r間)。 因此, 提前 DOMContentLoaded 是很有必要的。

一般的 CRP 流程是這樣的:

圖上有兩個灰色的方塊, 這兩個方塊是導(dǎo)致 CRP 時間變長的罪魁禍?zhǔn)祝?/p> 4. 減少 CRP , 出發(fā)!

縮短 CRP 就是盡快使 DOMContentLoaded 事件產(chǎn)生, 以便盡快執(zhí)行 APP 邏輯, 不要拖到 onload , 節(jié)約一分一秒! 因為 DOMContentLoaded 產(chǎn)生后, 就會構(gòu)建 Render Tree , 然后順?biāo)浦郏?用戶就能看到 APP 了。

不要引用過多的腳本文件!

在前幾年的前端開發(fā)中, 喜歡將對腳本的引用寫在 HTML 文件里, 這會導(dǎo)致 DOMContentLoaded 觸發(fā)大大延遲。 因為每一次解析一個腳本都會阻塞游覽器構(gòu)建 DOM 。 當(dāng)一個 HTML 文件引用幾十個外部腳本文件時, 那肯定是一場災(zāi)難! 所幸最近幾年各種前端打包工具使得這個問題得以解決。

在筆試騰訊的時候遇到一個問題: 使用 HTTP2 的時候還有沒有將 JS 文件打包的必要? 答案是: 有必要, 而且是必須的! 雖然 HTTP2 可以實現(xiàn)同一個 TCP 連接里的所有資源的并行傳輸(使用流)。 但是游覽器處理外部腳本文件的策略不會變! 所以依然有打包的必要!

將外部腳本的引用放到 HTML 文件的最后, 或者使用 defer 屬性。

即便你使用打包工具只引用了一個外部腳本文件, 但是如果這個腳本文件的傳輸延遲和執(zhí)行延遲, 會導(dǎo)致后面的非關(guān)鍵資源的請求被延遲, 雖然這不會減慢 APP 的首屏?xí)r間。 但是圖片等非關(guān)鍵資源的呈現(xiàn)時間卻被延遲了。 使用defer屬性可以將執(zhí)行時間推遲到 domContentLoaded 時間點(diǎn)后。 DOMContentLoaded也是這個時間點(diǎn)后觸發(fā)。那會先執(zhí)行 defer 還是先觸發(fā) DOMContentLoaded 事件? 在 Chrome 游覽器下的順序是先執(zhí)行 defer 再觸發(fā)DOMContentLoaded 事件。

對外部腳本使用 async 屬性

async 屬性告訴游覽器異步請求外部腳本文件, 不要阻塞在這里。 這樣可以使得游覽器繼續(xù)構(gòu)建 DOM 。 或者處理后面的資源請求。

將樣式文件請求置于 head 標(biāo)簽內(nèi)。

盡早在 HTML 文檔內(nèi)指定所有 CSS 資源,以便瀏覽器盡早發(fā)現(xiàn) link 標(biāo)記并盡早發(fā)出 CSS 請求。

盡量不要使用 @import 指令。

CSS 中的 @import 表示在一個樣式文件中導(dǎo)入另外一個樣式文件。 一個樣式文件 import 另外一個樣式文件時, 只有在這個樣式文件被收到且被解析完成才會 import 。 這樣會增加 CRP 長度。

內(nèi)聯(lián)樣式

使用 style 標(biāo)簽將樣式內(nèi)聯(lián)到 HTML 文件內(nèi), 雖然這樣做會增大 HTML 文件的體積。 但是卻可以減少 CRP 長度。 同時也避免了腳本代碼的執(zhí)行被阻塞的情況。

為什么沒有內(nèi)聯(lián)腳本呢? 因為絕大多數(shù)情況下不可能做到完全的內(nèi)聯(lián)樣式。 這個時候即便使用內(nèi)聯(lián)腳本也會因為 CSSOM 而被阻塞, 會阻止 DOM 構(gòu)建。 因而這并不是一種優(yōu)化措施。

減少你的關(guān)鍵資源的大小和數(shù)量

這是優(yōu)化最好的手段。

Else

這里并沒有提到如何去優(yōu)化應(yīng)用的邏輯來增加速度, 因為這并不屬于 CRP 。

5. 常用優(yōu)化工具安利。

Lighthouse

Lighthouse 是一個網(wǎng)絡(luò)應(yīng)用審核工具, 可以幫助你找到 CRP 的瓶頸所在。

Lighthouse 的截圖:

Navigation Time API

這個內(nèi)置的 API 可以幫助你記錄下各個時間點(diǎn)的具體時間值, 上面說的那幾個時間點(diǎn)都有。

6. 最后的最后

如果你覺得有哪里寫的不是很恰當(dāng)或你不能理解的, 可以在評論里告訴我。。

如果你覺得我寫的內(nèi)容對你有所幫助。 可以選擇關(guān)注我。

我的博客地址是: mrcode

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/50650.html

相關(guān)文章

  • 通俗方式理解關(guān)鍵渲染路徑

    摘要:縮短的長度能夠有效降低首屏?xí)r間。即便你使用打包工具只引用了一個外部腳本文件,但是如果這個腳本文件的傳輸延遲和執(zhí)行延遲,會導(dǎo)致后面的非關(guān)鍵資源的請求被延遲,雖然這不會減慢的首屏?xí)r間。 以通俗的方式理解關(guān)鍵渲染路徑 我在看了 google 的 Critical Rendering Path (中文)后, 想把 CRP(Critical Rendering Path) 用通俗易懂的方式描述出...

    Thanatos 評論0 收藏0
  • 前端修煉の道:第一個 HTML 頁面

    摘要:可見對一個頁面正確渲染很重要。和標(biāo)簽對用于標(biāo)識頁面的頭部區(qū)域,和之間的內(nèi)容都屬于頭部區(qū)域中的內(nèi)容。是一個輔助性標(biāo)簽,對頁面可以進(jìn)行很多方面的特性的設(shè)置。當(dāng)頁面沒有設(shè)置字符集時,瀏覽器會使用默認(rèn)的字符編碼顯示。 showImg(https://segmentfault.com/img/bVbjNkI?w=900&h=383); 往期回顧 在 1.2 節(jié)介紹 HTML 語言時講到:HTML...

    JasonZhang 評論0 收藏0
  • 一篇文章搞定前端面試

    摘要:客戶端發(fā)送包到服務(wù)器,并進(jìn)入狀態(tài),等待服務(wù)器確認(rèn)。再進(jìn)一步接收到客戶端的就進(jìn)入狀態(tài)。通常情況下連接就是連接,因此連接一旦建立通訊雙方開始互發(fā)數(shù)據(jù)進(jìn)行通信,直到其中一方或雙方斷開連接為止。統(tǒng)一資源定位符。 本文旨在用最通俗的語言講述最枯燥的基本知識 面試過前端的老鐵都知道,對于前端,面試官喜歡一開始先問些HTML5新增元素啊特性啊,或者是js閉包啊原型啊,或者是css垂直水平居中怎么實現(xiàn)...

    ISherry 評論0 收藏0
  • 一篇文章搞定前端面試

    摘要:客戶端發(fā)送包到服務(wù)器,并進(jìn)入狀態(tài),等待服務(wù)器確認(rèn)。再進(jìn)一步接收到客戶端的就進(jìn)入狀態(tài)。通常情況下連接就是連接,因此連接一旦建立通訊雙方開始互發(fā)數(shù)據(jù)進(jìn)行通信,直到其中一方或雙方斷開連接為止。統(tǒng)一資源定位符。 本文旨在用最通俗的語言講述最枯燥的基本知識 面試過前端的老鐵都知道,對于前端,面試官喜歡一開始先問些HTML5新增元素啊特性啊,或者是js閉包啊原型啊,或者是css垂直水平居中怎么實現(xiàn)...

    Airmusic 評論0 收藏0
  • 一篇文章搞定前端面試

    摘要:客戶端發(fā)送包到服務(wù)器,并進(jìn)入狀態(tài),等待服務(wù)器確認(rèn)。再進(jìn)一步接收到客戶端的就進(jìn)入狀態(tài)。通常情況下連接就是連接,因此連接一旦建立通訊雙方開始互發(fā)數(shù)據(jù)進(jìn)行通信,直到其中一方或雙方斷開連接為止。統(tǒng)一資源定位符。 本文旨在用最通俗的語言講述最枯燥的基本知識 面試過前端的老鐵都知道,對于前端,面試官喜歡一開始先問些HTML5新增元素啊特性啊,或者是js閉包啊原型啊,或者是css垂直水平居中怎么實現(xiàn)...

    lavnFan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<