成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

前端性能優化之關鍵路徑渲染優化

開發 前端
當 DOM 樹和 CSSOM 樹都構建完成的時候,他們就會合并在一起構建 render tree,因為要在頁面上渲染不僅需要這個頁面的結構,也需要知道整個頁面的樣式,所以 render tree 是 DOM 樹和 CSSOM 樹的結合體,有了 render tree,瀏覽器才能知道把什么內容按照什么樣式渲染在屏幕上。

瀏覽器加載流程

瀏覽器在渲染頁面時需要將 HTML 標記轉化成 DOM 對象

CSS 則會被轉化成 CSSOM 對象

DOM 和 CSSOM 是獨立的樹形結構,

當 DOM 樹和 CSSOM 樹都構建完成的時候,他們就會合并在一起構建 render tree,因為要在頁面上渲染不僅需要這個頁面的結構,也需要知道整個頁面的樣式,所以 render tree 是 DOM 樹和 CSSOM 樹的結合體,有了 render tree,瀏覽器才能知道把什么內容按照什么樣式渲染在屏幕上。

瀏覽器從獲取 HTML 到最終在屏幕上顯示內容需要完成以下步驟:

  1. 處理 HTML 標記并構建 DOM 樹。
  2. 處理 CSS 標記并構建 CSSOM 樹。
  3. 將 DOM 與 CSSOM 合并成一個 render tree。
  4. 根據渲染樹來布局,以計算每個節點的幾何信息。
  5. 將各個節點繪制到屏幕上。

經過以上整個流程我們才能看見屏幕上出現渲染的內容,優化關鍵渲染路徑就是指最大限度縮短執行上述第 1 步至第 5 步耗費的總時間,讓用戶最快的看到首次渲染的內容。

另外,這是一個漸進的過程。為達到更好的用戶體驗,呈現引擎會力求盡快將內容顯示在屏幕上。它不必等到整個 HTML 文檔解析完畢之后,就會開始構建呈現樹和設置布局。在不斷接收和處理來自網絡的其余內容的同時,呈現引擎會將部分內容解析并顯示出來,因為 HTML 采用基于流的布局模型,這意味著大多數情況下只要一次遍歷就能計算出幾何信息。處于流中靠后位置元素通常不會影響靠前位置元素的幾何特征,因此布局可以按從左至右、從上至下的順序遍歷文檔。但是也有例外情況,比如 HTML 表格的計算就需要不止一次的遍歷。

阻塞渲染的因素

外部樣式表

從上面的整個流程我們已經知道,瀏覽器的渲染需要 render tree, render tree 需要 CSSOM 樹才行,所以樣式表的加載是會阻塞頁面的渲染的,如果有一個外部的樣式表處于下載中,那么即使 HTML 已經下載完畢,也會等待外部樣式表下載并解析完畢才會開始構建 render tree。

腳本

腳本就更麻煩了,先明確一點, JS 引擎和 UI 的渲染引擎是互斥的,所以當腳本在執行的時候瀏覽器要將控制權就給 JS 引擎,等到 JS 執行完畢再還給 UI 引擎,不論這個腳本是以何種形式加載的,在執行時均會阻塞 UI 的渲染。

接下來分別看不同形式加載的腳本對頁面渲染的阻塞情況:

內聯腳本

<script>...</script>

內聯的腳本隨著 HTML 一起下載,在開始執行時已經完成了 字節 → 字符 → 令牌 → 節點 → 對象模型 的整個過程,所以不存在下載的時間(其實也不能這么說,下載的時間算在了 HTML 的下載時間中),執行時是會阻塞關鍵渲染路徑的。

外部腳本

<script src="sample.js"></script>

外部腳本的整個加載過程及執行過程都是阻塞關鍵渲染路徑的。

帶 defer 和 async 的外部腳本

<script src="sample.js" defer></script>
<script src="sample.js" async></script>

帶 defer/async 的腳本會與 HTML 并行下載,下載的過程不會阻塞 DOM 的構建,但是執行是會的,不同的是 defer 是在 DomContentLoaded 之前執行,async 是加載完之后立刻執行。

defer/async 的腳本在下載期間不會阻塞頁面解析不是一個技術原因而是一個選擇,因為內聯腳本/外部腳本是要等待他們執行,所以不得不等待他們下載。而頁面并不需要等待 defer/async 的腳本,所以他們的下載與頁面的解析是并行的。

動態生成的腳本

var dynamicScript = document.creatElement('script')
dynamicScript.src = 'sample.js'
document.head.appendChild(dynamicScript)
dynamicScript.onload = function(){...}

動態生成的腳本的下載過程不會阻塞頁面的解析,執行會阻塞解析,有點 async 的感覺。

腳本與樣式表的依賴關系

腳本不僅能夠訪問 DOM 元素,還能訪問 DOM 的樣式,如果將要執行腳本時瀏覽器尚未完成 CSSOM 的下載及構建,瀏覽器將延遲腳本執行和 DOM 構建,直至其完成 CSSOM 的下載和構建。

所以,CSSOM 的構建會阻塞 HTML 的渲染,也會阻塞 JS 的執行,JS 的下載與執行(內聯及外部樣式表)也會阻塞 HTML 的渲染。

優化方法

為盡快完成首次渲染,我們需要最大限度減小以下三種可變因素:

  • 關鍵資源的數量:可能阻止網頁首次渲染的資源。
  • 關鍵路徑長度:獲取所有關鍵資源所需的往返次數或總時間。
  • 關鍵字節的數量:實現網頁首次渲染所需的總字節數,它是所有關鍵資源傳送文件大小的總和。我們包含單個 HTML 頁面的第一個示例包含一項關鍵資源(HTML 文檔);關鍵路徑長度也與 1 次網絡往返相等(假設文件較小),而總關鍵字節數正好是 HTML 文檔本身的傳送大小。

優化關鍵渲染路徑的常規步驟如下:

  1. 對關鍵路徑進行分析和特性描述:資源數、字節數、長度。
  2. 最大限度減少關鍵資源的數量:刪除它們,延遲它們的下載,將它們標記為異步等。
  3. 優化關鍵字節數以縮短下載時間(往返次數)。
  4. 優化其余關鍵資源的加載順序:您需要盡早下載所有關鍵資產,以縮短關鍵路徑長度。

關鍵 CSS

上面已經分析過了,樣式表會阻塞渲染,在加載完畢之前是不會顯示的,為了讓用戶以最快的速度看到頁面上的內容,可以將頁面的某一部分的樣式抽離出來,單獨放在一個樣式表中或者內聯在頁面中,這樣的樣式稱為關鍵樣式,這部分樣式會優先它可以是頁面的骨架屏或者是用戶剛加載進頁面時看到的首屏的內容。

<!doctype html>
<head>
<style> /* inlined critical CSS */ </style>
<script> loadCSS('non-critical.css'); </script>
</head>
<body>
...body goes here
</body>
</html>

預加載 —— preload & prefetch

使用 preload meta 來提升資源加載的優先級。preload 的定義

preload is a declarative fetch, allowing you to force the browser to make a request for a resource without blocking the document’s onload event.

注意和 prefetch 的區別

<link rel=“prefetch”> is a directive that tells a browser to fetch a resource that will probably be needed for the next navigation. That mostly means that the resource will be fetched with extremely low priority

preload 會提升資源的優先級因為它標明這個資源是本頁肯定會用到 —— 本頁優先

prefetch 會降低這個資源的優先級因為它標明這個資源是下一頁可能用到的 —— 為下一頁提前加載

preload 最大的作用就是將下載與執行分離,并且將下載的優先級提到了一個很高的地步,再由我們去控制資源執行的位置。

加速樣式表下載

樣式表是阻塞頁面呈現的(注意是呈現,不是解析),正常通過 link 加載的外部樣式表要等下載,構建 CSSOM 樹才會讓頁面呈現完成,但是 preload 能夠讓樣式表的下載和呈現分離。

試想,當你在頁面的 head 中寫了如下的兩個樣式表:

<link href="critial.css" rel="stylesheet" />
<link href="non-critial.css" rel="stylesheet" />

第一個是關鍵 CSS,第二個不是關鍵 CSS,當頁面解析了這兩個 link 標簽后開始下載,但是即使 critical.css 下載解析完畢也不會呈現頁面,因為頁面還要下載和解析 non-critical.css。

這時候,就要將 non-critial.css 作為預加載,當樣式表作為被 preload 后,他就不會再阻塞頁面的呈現,也就是所謂的異步下載,修改后的代碼如下:

<link href="critial.css" rel="stylesheet" />
<link rel="preload" href="non-critial.css" as="style" />
<link href="non-critial.css" rel="stylesheet" />

如此一來,頁面在解析完 critical.css 之后就會呈現(暫不考慮腳本),而 non-critial 也在下載,但是并不阻塞頁面,指導它下載和解析完畢后才會應用到頁面上。

現在并不是所有的瀏覽器都支持 preload,我們可以用 loadCSS 這個庫來做 polyfill,其實現的思路也是遍歷所有帶 preload 和 as 的標簽,然后修改標簽的 media 為不匹配任何條件并開始下載,在下載完畢后再還原該 link 原來的 media 標簽將它應用。

加速腳本下載

preload 將腳本的加載及執行分離,加了 preload 的 <link> 標簽的作用是將腳本提到高優先級盡快完成下載,但并未執行。

<link rel="preload" href="http://cdn.staticfile.org/jquery/3.2.1/jquery.min.js" as="script" />

還需要在你想要他執行的地方引入一個正常的 <script> 標簽執行這個腳本

<script src="http://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>

否則 chrome 大約會在 3s 后報一個 warning 來提醒你這個資源被浪費了完全沒有被使用到。

preload 的功能聽起來很像被 defer 的腳本,但是:

  1. defer 無法控制腳本執行的時機,是在 DOMContentLoaded 執行前觸發
  2. defer 會阻塞 DOMContentLoaded 事件
  3. defer 會阻塞 onload 事件,preload 不會阻塞 onload 事件
  4. defer 的腳本下載的優先級是 low,preload 的腳本優先級是 high

根據腳本在文檔中的位置不同和他們是否是 async,defer 和阻塞,它們會有不同的優先級:

  • 阻塞腳本在第一個圖片前發起請求的優先級為:Medium(DevTools 中為 high)
  • 阻塞腳本在第一個圖片后發情請求的優先級為:Low(DevTools 中為 Medium)
  • async/defer/動態插入的腳本(不論他們在文檔中的什么位置)的優先級為:Lowest(DevTools 中為 Low)

我們以掘金的首頁為例:

可以看到 high 的全是寫在 HTML 中進行加載的靜態資源,Low 的都是 thunk 在 JS 中的腳本,是為其他頁面預加載的。

加速字體下載

自定義的字體在加載之前會處于 FOIT(Flash of Invisible Text)現象,具體的可以看 這篇文章,雖然我們可以使用類似 webFont 一類的庫來控制字體的閃現和添加鉤子函數,但最佳解決方法還是讓字體的加載達到最快的速度。

使用 preload 也可以來加速字體的下載,在 head 中聲明 preload,比先下載樣式表再從中讀到 @font-face 的 src 再去加載要快得多。

<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">

但是要注意

preload 字體不帶 crossorigin 也將會二次獲取! 確保你對 preload 的字體添加 crossorigin 屬性,否則他會被下載兩次,這個請求使用匿名的跨域模式。這個建議也適用于字體文件在相同域名下,也適用于其他域名的獲取(比如說默認的異步獲取)。

preload 如果不帶 crossorigin meta ,默認情況下 (即未指定 crossorigin 屬性時), CORS 根本不會使用,這樣 http 的 request header 中就不會有 origin,默認不去跨域,但是 @font-face 中去加載字體是默認跨域請求的,所以會造成兩次的 request header 不同,無法命中緩存,造成重復請求。

解決方法就是帶上 crossorigin,

<link rel="preload" as="font" href="http://at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin>
<link rel="preload" as="font" href="http://at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin="anonymous">
<link rel="preload" as="font" href="http://at.alicdn.com/t/font_327081_19o9k2m6va4np14i.woff" crossorigin="fi3ework">

空關鍵字和無效關鍵字都會被當做 anonymous。

其他資源

preload 不僅可以將這些在 head 中的資源加速,還可以提前加載一些隱藏在 CSS 和 JS 中的資源,比如剛才隱藏在 CSS 中的字體資源,或者 JS 中請求的資源。

preload 的標簽可以動態生成,這意味著在任何時候你都可以在頁面中提前加載但不執行一個腳本,然后通過動態腳本來立刻執行它。

var preload = document.createElement("link");
link.href = "myscript.js";
link.rel = "preload";
link.as = "script";
document.head.appendChild(link);
var script = document.createElement("script");
script.src = "myscript.js";
document.body.appendChild(script);

媒體查詢

現在的頁面基本上都具有響應式設計,即針對移動端或桌面端會采用 media 進行媒體查詢,有兩種包含媒體查詢的 CSS 代碼的方法:1. 將需要媒體查詢的代碼和基礎樣式代碼放在同一文件中,使用 @media 來使媒體查詢生效。 2. 將需要媒體查詢的代碼放在單獨的一個外部樣式表中,使用 media meta 對需要媒體查詢的 link 進行控制。

這兩種方法各有好處,如果需要媒體查詢的代碼量很小,那么和基礎樣式放在一起也沒有關系,可以節省一次 HTTP 請求。如果比較大的話,那么就會讓樣式表的體積增加,造成 FOUC 的時間變長,這時候更適合使用第二種。

另外請注意“阻塞渲染”僅是指瀏覽器是否需要暫停網頁的首次渲染,直至該資源準備就緒。無論哪一種情況,瀏覽器仍會下載 CSS 資源,但是不阻塞渲染的資源優先級較低。

優先級較低意味著瀏覽器在解析 HTML 時發現要下載這個樣式表,但并不一定會立刻開始下載,而是可能會將它滯后一段時間再下載(等級低沒人權),從 DevTools 上也可以看到 Highest 和 Lowest 的區別。

如果媒體查詢的樣式表符合當前的頁面,那么媒體查詢的樣式表也會阻塞關鍵路徑渲染(就好像他是個正常的一樣),同時,它的下載優先級也會恢復到最高(恢復人權)。

media 配合 preload 能做到響應式加載資源,如下代碼,分別是兩副圖片適配移動端與 PC 端,如果不加 preload 的話,那么其中一幅就會以 Lowest 的等級延遲加載,但是如果我們是一個移動端優先的網站,不希望用戶浪費流量及網速下載PC 端的大圖的話,就在每個 link 上加上 preload 即可,只有在打開網頁時符合 media 的資源會被加載,不符合 media 的資源始終不會被加載,即使后面將瀏覽器的寬度拉寬也不會加載。

<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
<link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)">

如果用戶真的拉寬了屏幕,或者切換端設備,可以使用 Window.matchMedia,來進行 media 的匹配。

var mediaQueryList = window.matchMedia("(max-width: 600px)");
var header = document.querySelector('header');

if(mediaQueryList.matches) {
header.style.backgroundImage = 'url(bg-image-narrow.png)';
} else {
header.style.backgroundImage = 'url(bg-image-wide.png)';
}

DNS 預解析 —— dns-prefetch

dns-prefetch 的使用方法更加簡單:

<link rel="dns-prefetch" href="http://host_name_to_prefetch.com">

link 標簽的 rel 設定為 dns-prefetch,href 設定為需要預加載的主機域名即可。

在講 dns-prefetch 之前,先復習一遍 DNS 的作用及可以優化的點才能了解 dns-prefetch 帶來的好處。

網絡通訊大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所以計算機在網絡上進行通訊時只能識別如“202.96.134.133”之類的IP地址,而不能認識域名。我們無法記住10個以上IP地址的網站,所以我們訪問網站時,更多的是在瀏覽器地址欄中輸入域名,就能看到所需要的頁面,這是因為有一個叫“DNS服務器”的計算機自動把我們的域名“翻譯”成了相應的IP地址,然后調出IP地址所對應的網頁。

一圖流表達如下,其中 3, 4, 5, 6, 7 都屬于 DNS 解析的過程,也是 dns-prefetch 發揮作用的地方。

dns-prefetch 主要用來在用戶點擊一個鏈接之前解析對應的域名,這會自動去調用用戶瀏覽器的解析機制。瀏覽器會在用戶瀏覽網頁時多線程完成預加載,當用戶真正點擊的時候就節省了用戶等待域名解析的時間。

Chromium 的官方文檔中很詳細的介紹了 pre-fetch:

  1. Chromium 會根據頁面中超鏈接的 href 去尋找主機名自動去 prefetch
  2. 如果訪問的鏈接被重定向,那么瀏覽器可能無法自動識別出真正的主機進行 prefetch,此時需要我么手工預加載,也就是使用 prefetch 標簽來指定主機。(這也是決定是否使用 dns-prefetch 的判斷方法)
  3. 預加載不會對頁面渲染造成損害,因為 Chromium 有8個專門用來預加載的線程。
  4. dns-prefetch 帶來的網絡消耗是很小的
  5. Each request typically involves sending a single UDP packet that is under 100 bytes out, and getting back a response that is around 100 bytes
  6. 但是用最小的網絡開銷代價可以換來較好的用戶體驗。
  7. 默認情況下,Chromium 和 Firefox 出于安全考慮會關閉在 https 下的自動預加載,可以通過指定 meta http-equiv 來開啟自動預加載。
  8. <meta http-equiv="x-dns-prefetch-control" cnotallow="on">
  9. PS: 如果通過 meta 顯示的關閉了預加載,之后將無法再次開啟預加載。

拿知乎舉個例子,打開知乎,進入控制臺,搜索 dns-prefetch

發現知乎用了如下的 link,都是知乎的靜態資源服務器,因為在沒有緩存(假設沒有打開過知乎)時打開某個知乎頁面,如果該頁面有圖片,并且是從以上的域名獲取的話 dns-prefetch 就不會起作用。如果沒有圖片,那么上面的 dns-prefetch 就會解析域名,等到打開一個有圖的知乎頁面時 DNS 解析已經完成了。

DNS 預解析 + TCP + TLS —— preconnect

提前加載整個頁面 —— prerender

以上兩者詳見:

  • [譯] 資源提示 —— 什么是 Preload,Prefetch 和 Preconnect?
  • [譯] Prefetch & preconnect-dns 的優先級 性能優化?

文章出自:??前端餐廳??,如有轉載本文請聯系前端餐廳今日頭條號。

github:??https://github.com/zuopf769??

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2018-06-27 08:21:31

前端Web渲染

2015-09-16 13:54:30

Android性能優化渲染

2022-01-09 16:45:36

前端性能優化編程

2022-11-16 12:03:13

性能優化前端

2022-05-17 09:02:30

前端性能優化

2020-10-16 10:40:39

前端性能可視化

2019-11-01 14:00:58

前端性能優化代碼

2020-10-16 09:00:12

前端開發技術

2021-07-29 14:20:34

網絡優化移動互聯網數據存儲

2016-12-08 10:57:08

渲染引擎前端優化

2023-11-18 19:46:07

GPU架構

2022-12-12 09:01:13

2018-11-14 19:30:57

前端Javascript性能優化

2020-03-09 16:43:06

腳本語言瀏覽器JavaScript

2020-05-27 09:41:10

前端性能邊緣計算

2021-11-29 11:13:45

服務器網絡性能

2022-02-16 14:10:51

服務器性能優化Linux

2018-01-09 16:56:32

數據庫OracleSQL優化

2019-12-13 10:25:08

Android性能優化啟動優化

2022-03-02 11:13:50

Web前端開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品资源 | 性xxxxx| 国产精品一区二区在线观看 | 一区二区中文字幕 | 91视频在线 | 成人网在线观看 | 翔田千里一区二区 | av手机免费在线观看 | 欧美a在线| 中文字幕亚洲欧美 | 五月婷婷在线视频 | av中文字幕网站 | 亚洲成人一区 | 玖玖在线精品 | 亚洲成人自拍 | 亚洲成人黄色 | 99reav| 欧美日韩视频在线第一区 | 911网站大全在线观看 | 日韩亚洲一区二区 | 欧美成人免费 | 正在播放亚洲 | 精品成人av | 久久精片 | 精品一区二区视频 | 免费亚洲成人 | 操视频网站 | 久久在线 | 欧美视频免费 | 夜夜夜夜夜夜曰天天天 | 99re视频在线观看 | 91精品国产综合久久精品 | 日本超碰 | 精品一区二区电影 | 日韩一区av | 中文字幕第一页在线 | 欧美日韩国产一区二区三区 | 精品国产乱码久久久久久蜜臀 | 亚洲成人二区 | 亚洲成人免费观看 | 紧缚调教一区二区三区视频 |