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