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