摘要:修改瀏覽器渲染因為的阻塞使得解析停止,下載完成之前,頁面無法顯示任何東西。瀏覽器渲染解析到文件時出現阻塞。我們把調整到尾部瀏覽器渲染這是頁面可以渲染了,但是沒有樣式。
本文示例源代碼請戳github博客,建議大家動手敲敲代碼。前言
瀏覽器渲染頁面的過程
從耗時的角度,瀏覽器請求、加載、渲染一個頁面,時間花在下面五件事情上:
DNS 查詢
TCP 連接
HTTP 請求即響應
服務器響應
客戶端渲染
本文討論第五個部分,即瀏覽器對內容的渲染,這一部分(渲染樹構建、布局及繪制),又可以分為下面五個步驟:
處理 HTML 標記并構建 DOM 樹。
處理 CSS 標記并構建 CSSOM 樹
將 DOM 與 CSSOM 合并成一個渲染樹。
根據渲染樹來布局,以計算每個節點的幾何信息。
將各個節點繪制到屏幕上。
需要明白,這五個步驟并不一定一次性順序完成。如果 DOM 或 CSSOM 被修改,以上過程需要重復執行,這樣才能計算出哪些像素需要在屏幕上進行重新渲染。實際頁面中,CSS 與 JavaScript 往往會多次修改 DOM 和 CSSOM。
1、瀏覽器的線程在詳細說明之前我們來看一下瀏覽器線程。這將有助于我們理解后續內容。
瀏覽器是多線程的,它們在內核制控下相互配合以保持同步。一個瀏覽器至少實現三個常駐線程:JavaScript 引擎線程,GUI 渲染線程,瀏覽器事件觸發線程。
GUI 渲染線程:負責渲染瀏覽器界面 HTML 元素,當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時,該線程就會執行。在 Javascript 引擎運行腳本期間,GUI 渲染線程都是處于掛起狀態的,也就是說被”凍結”了。
JavaScript 引擎線程:主要負責處理 Javascript 腳本程序。
定時器觸發線程:瀏覽器定時計數器并不是由 JavaScript 引擎計數的, JavaScript 引擎是單線程的, 如果處于阻塞線程狀態就會影響記計時的準確, 因此瀏覽器通過多帶帶線程來計時并觸發定時。
事件觸發線程:當一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待 JS 引擎的處理。這些事件包括當前執行的代碼塊如定時任務、瀏覽器內核的其他線程如鼠標點擊、AJAX 異步請求等。由于 JS 的單線程關系所有這些事件都得排隊等待 JS 引擎處理。定時塊任何和 ajax 請求等這些異步任務,事件觸發線程只是在到達定時時間或者是 ajax 請求成功后,把回調函數放到事件隊列當中。
異步 HTTP 請求線程:在 XMLHttpRequest 在連接后是通過瀏覽器新開一個線程請求, 將檢測到狀態變更時,如果設置有回調函數,異步線程就產生狀態變更事件放到 JavaScript 引擎的處理隊列中等待處理。在發起了一個異步請求時,http 請求線程則負責去請求服務器,有了響應以后,事件觸發線程再把回到函數放到事件隊列當中。
2、構建DOM樹與CSSOM樹瀏覽器從網絡或硬盤中獲得HTML字節數據后會經過一個流程將字節解析為DOM樹:
編碼: 先將HTML的原始字節數據轉換為文件指定編碼的字符。
令牌化: 然后瀏覽器會根據HTML規范來將字符串轉換成各種令牌(如、這樣的標簽以及標簽中的字符串和屬性等都會被轉化為令牌,每個令牌具有特殊含義和一組規則)。令牌記錄了標簽的開始與結束,通過這個特性可以輕松判斷一個標簽是否為子標簽(假設有與兩個標簽,當標簽的令牌還未遇到它的結束令牌就遇見了標簽令牌,那么就是的子標簽)。
生成對象: 接下來每個令牌都會被轉換成定義其屬性和規則的對象(這個對象就是節點對象)
構建完畢: DOM樹構建完成,整個對象集合就像是一棵樹形結構。可能有人會疑惑為什么DOM是一個樹形結構,這是因為標簽之間含有復雜的父子關系,樹形結構正好可以詮釋這個關系(CSSOS同理,層疊樣式也含有父子關系。例如: div p {font-size: 18px},會先尋找所有p標簽并判斷它的父標簽是否為div之后才會決定要不要采用這個樣式進行渲染)。
整個DOM樹的構建過程其實就是: 字節 -> 字符 -> 令牌 -> 節點對象 -> 對象模型,
下面將通過一個示例HTML代碼與配圖更形象地解釋這個過程。
Critical Path Hello web performance students!
瀏覽器渲染 1111111
222222
3333333
style.css
#header{ color: red; }a.js、b.js暫時為空
6、HTML 是否解析一部分就顯示一部分
可以看到,服務端將對a.js的請求延遲5秒返回。Server啟動后,在chrome瀏覽器中打開http://127.0.0.1:8080/index.html
我們打開chrome的調試面板
第一次解析html的時候,外部資源好像是一起請求的,說資源是預解析加載的,就是說style.css和b.js是a.js造成阻塞的時候才發起的請求,圖中也是可以解釋得通,因為第一次Parse HTML的時候就遇到阻塞,然后預解析就去發起請求,所以看起來是一起請求的。我們修改一下html代碼
瀏覽器渲染 1111111
222222
3333333
7、js文件的位置對HTML解析有什么影響 7.1 js文件在頭部加載。
因為a.js的延遲,解析到a.js所在的script標簽的時候,a.js還沒有下載完成,阻塞并停止解析,之前解析的已經繪制顯示出來了。當a.js下載完成并執行完之后繼續后面的解析。當然,瀏覽器不是解析一個標簽就繪制顯示一次,當遇到阻塞或者比較耗時的操作的時候才會先繪制一部分解析好的。修改index.html:
瀏覽器渲染 1111111
222222
3333333
7.2、js文件在中間加載。
因為a.js的阻塞使得解析停止,a.js下載完成之前,頁面無法顯示任何東西。瀏覽器渲染 1111111
222222
3333333
7.3、js文件在尾部加載。
解析到js文件時出現阻塞。阻塞后面的解析,導致后面的不能很快的顯示。瀏覽器渲染 1111111
222222
3333333
解析到a.js部分的時候,頁面要顯示的東西已經解析完了,a.js不會影響頁面的呈現速度。
由上面我們可以總結一下
直接引入的 JS 會阻塞頁面的渲染(GUI 線程和 JS 線程互斥)
JS 不阻塞資源的加載
JS 順序執行,阻塞后續 JS 邏輯的執行
下面我們來看下異步js
7.4、async和defer的作用是什么?有什么區別?接下來我們對比下 defer 和 async 屬性的區別:
其中藍色線代表JavaScript加載;紅色線代表JavaScript執行;綠色線代表 HTML 解析。情況1
沒有 defer 或 async,瀏覽器會立即加載并執行指定的腳本,也就是說不等待后續載入的文檔元素,讀到就加載并執行。
情況2 (異步下載)
async 屬性表示異步執行引入的 JavaScript,與 defer 的區別在于,如果已經加載好,就會開始執行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發之后。需要注意的是,這種方式加載的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發之前或之后執行,但一定在 load 觸發之前執行。
情況3 (延遲執行)
defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 加載時 HTML 并未停止解析,這兩個過程是并行的。整個 document 解析完畢且 defer-script 也加載完成之后(這兩件事情的順序無關),會執行所有由 defer-script 加載的 JavaScript 代碼,然后觸發 DOMContentLoaded 事件。
defer 與相比普通 script,有兩點區別:
載入 JavaScript 文件時不阻塞 HTML 的解析,執行階段被放到 HTML 標簽解析完成之后。
在加載多個JS腳本的時候,async是無順序的加載,而defer是有順序的加載。
8、css文件的影響服務端將style.css的相應也設置延遲。
fs.readFile("style.css", "utf-8", function (err, data) { res.writeHead(200, {"Content-Type": "text/css"}); setTimeout(function () { res.write(data); res.end() }, 5000) })瀏覽器渲染 1111111
222222
3333333
可以看出來,css文件不會阻塞HTML解析,但是會阻塞渲染,導致css文件未下載完成之前已經解析好html也無法先顯示出來。
我們把css調整到尾部
瀏覽器渲染 1111111
222222
3333333
這是頁面可以渲染了,但是沒有樣式。直到css加載完成
以上我們可以簡單總結。
CSS 放在 head 中會阻塞頁面的渲染(頁面的渲染會等到 css 加載完成)
CSS 阻塞 JS 的執行 (因為 GUI 線程和 JS 線程是互斥的,因為有可能 JS 會操作 CSS)
CSS 不阻塞外部腳本的加載(不阻塞 JS 的加載,但阻塞 JS 的執行,因為瀏覽器都會有預先掃描器)
參考
瀏覽器渲染過程與性能優化
聊聊瀏覽器的渲染機制
你不知道的瀏覽器頁面渲染機制文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103661.html
摘要:模塊和將下面的渲染機制,安全機制,插件機制等等隱藏起來,提供一個接口層。進行網頁的渲染進程,可能有多個。最后進程將結果由線程傳遞給進程最后,進程接收到結果并將結果繪制出來。 這是之前在簡書上面的處女作,也搬過來了,以后就一直在 segmentfault 上面寫文章了,webkit技術內幕-朱永盛是我大四買的書,很舊的一本書了,當時只看了一點點,一直沒繼續看完它,現在才看完,,,說來慚愧...
摘要:前端頁面渲染機制筆記瀏覽器基礎結構用戶界面用戶所看到及與之交互的功能組件,如地址欄返回前進按鈕瀏覽器引擎用戶界面和呈現引擎之間傳遞指令渲染引擎呈現引擎負責解析用戶請求的內容網絡負責處理網絡相關的事物后端負責繪制提示框等瀏覽器組件,底層使用 前端頁面渲染機制-筆記 瀏覽器基礎結構 1.用戶界面(user interface):用戶所看到及與之交互的功能組件,如地址欄、返回、前進按鈕 2...
摘要:前端頁面渲染機制筆記瀏覽器基礎結構用戶界面用戶所看到及與之交互的功能組件,如地址欄返回前進按鈕瀏覽器引擎用戶界面和呈現引擎之間傳遞指令渲染引擎呈現引擎負責解析用戶請求的內容網絡負責處理網絡相關的事物后端負責繪制提示框等瀏覽器組件,底層使用 前端頁面渲染機制-筆記 瀏覽器基礎結構 1.用戶界面(user interface):用戶所看到及與之交互的功能組件,如地址欄、返回、前進按鈕 2...
摘要:事件循環機制首先區分進程和線程進程是資源分配的最小單位系統會給它分配內存不同的進程之間是可以同學的,如管道命名管道消息隊列一個進程里有單個或多個線程瀏覽器是多進程的,因為系統給它的進程分配了資源內存打開會有一個主進程,每打開一個頁就有一個獨 JS JavaScript事件循環機制 首先區分進程和線程 進程是cpu資源分配的最小單位(系統會給它分配內存) 不同的進程之間是可以同學的,如...
摘要:如果看完本文后,還對進程線程傻傻分不清,不清楚瀏覽器多進程瀏覽器內核多線程單線程運行機制的區別。因此準備梳理這塊知識點,結合已有的認知,基于網上的大量參考資料,從瀏覽器多進程到單線程,將引擎的運行機制系統的梳理一遍。 前言 見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。 ----------超長文+多圖預警,需要花費不少時間。---------- 如果看完本文后,還...
摘要:渲染機制瀏覽器渲染機制什么是及作用告訴瀏覽器文件是什么文檔類型,瀏覽器根據它來判斷用什么引擎來解析渲染文件。觸發改動改動例當添加時,最好一次添加,避免多次。 渲染機制 瀏覽器 1. 渲染機制 什么是 DOCTYPE 及作用 DTD 告訴瀏覽器文件是什么文檔類型,瀏覽器根據它來判斷用什么引擎來解析渲染文件。DOCTYPE 用來聲明文檔類型和 DTD 規范。 瀏覽器是怎么渲染過程show...
閱讀 1110·2021-09-22 16:04
閱讀 1494·2019-08-30 15:43
閱讀 1097·2019-08-29 14:01
閱讀 3437·2019-08-26 12:19
閱讀 3351·2019-08-26 12:15
閱讀 1443·2019-08-26 12:13
閱讀 3264·2019-08-23 17:00
閱讀 1482·2019-08-23 15:38