成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

Thanatos / 1508人閱讀

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

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

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

1. 什么是 CRP ?

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

CRP 大概包括兩個(gè)部分: 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)計(jì)算精確位置(布局)。

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

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

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

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

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

3. 深入 CRP

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

什么是 CRP 長度?

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

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

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

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

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

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

如圖:

talk is cheap show me the code

HTML 文件:




  
  
  
  
  Test


  
emma
Emma.Watson

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

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

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

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

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

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

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

一般的 CRP 流程是這樣的:

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

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

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

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

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

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

即便你使用打包工具只引用了一個(gè)外部腳本文件, 但是如果這個(gè)腳本文件的傳輸延遲和執(zhí)行延遲, 會(huì)導(dǎo)致后面的非關(guān)鍵資源的請求被延遲, 雖然這不會(huì)減慢 APP 的首屏?xí)r間。 但是圖片等非關(guān)鍵資源的呈現(xiàn)時(shí)間卻被延遲了。 使用defer屬性可以將執(zhí)行時(shí)間推遲到 domContentLoaded 時(shí)間點(diǎn)后。 DOMContentLoaded也是這個(gè)時(shí)間點(diǎn)后觸發(fā)。那會(huì)先執(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 表示在一個(gè)樣式文件中導(dǎo)入另外一個(gè)樣式文件。 一個(gè)樣式文件 import 另外一個(gè)樣式文件時(shí), 只有在這個(gè)樣式文件被收到且被解析完成才會(huì) import 。 這樣會(huì)增加 CRP 長度。

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

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

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

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

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

Else

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

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

Lighthouse

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

Lighthouse 的截圖:

Navigation Time API

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

6. 最后的最后

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

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

我的博客地址是: mrcode

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

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

相關(guān)文章

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

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

    jzman 評論0 收藏0
  • 前端修煉の道:第一個(gè) HTML 頁面

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

    lavnFan 評論0 收藏0

發(fā)表評論

0條評論

Thanatos

|高級講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<