前端浏览器渲染原理及优化
文章目錄
- 一.瀏覽器組成
- 1. 對(duì)瀏覽器內(nèi)核的理解
- 2. 瀏覽器的主要組成部分
- 二、瀏覽器渲染原理
- 1.瀏覽器的渲染過(guò)程
- 步驟一:
- 步驟二:
- 步驟三:
- 步驟四:
- 步驟五:
- 2.相關(guān)概念
- ①重排(更新元素的幾何屬性)
- ②重繪(更細(xì)元素的繪制屬性)
- ③直接合成階段
- 三.瀏覽器渲染優(yōu)化
- **(1)針對(duì)JavaScript:**
- **(2)針對(duì)CSS:**
- **(3)針對(duì)DOM樹(shù)、CSSOM樹(shù):**
- **(4)減少回流與重繪:**
一.瀏覽器組成
瀏覽器的主要功能是將用戶選擇的 web 資源呈現(xiàn)出來(lái),它需要從服務(wù)器請(qǐng)求資源,并將其顯示在瀏覽器窗口中,資源的格式通常是 HTML,也包括 PDF、image 及其他格式。用戶用 URI(Uniform Resource Identifier 統(tǒng)一資源標(biāo)識(shí)符)來(lái)指定所請(qǐng)求資源的位置。
1. 對(duì)瀏覽器內(nèi)核的理解
瀏覽器內(nèi)核主要分成兩部分:
- 渲染引擎的職責(zé)就是渲染,即在瀏覽器窗口中顯示所請(qǐng)求的內(nèi)容。默認(rèn)情況下,渲染引擎可以顯示 html、xml 文檔及圖片,它也可以借助插件顯示其他類型數(shù)據(jù),例如使用 PDF 閱讀器插件,可以顯示 PDF 格式。
- JS 引擎:解析和執(zhí)行 javascript 來(lái)實(shí)現(xiàn)網(wǎng)頁(yè)的動(dòng)態(tài)效果。
最開(kāi)始渲染引擎和 JS 引擎并沒(méi)有區(qū)分的很明確,后來(lái) JS 引擎越來(lái)越獨(dú)立,內(nèi)核就傾向于只指渲染引擎。
2. 瀏覽器的主要組成部分
- ?戶界? - 包括地址欄、前進(jìn)/后退按鈕、書簽菜單等。除了瀏覽器主窗?顯示的您請(qǐng)求的??外,其他顯示的各個(gè)部分都屬于?戶界?。
- 瀏覽器引擎 - 在?戶界?和呈現(xiàn)引擎之間傳送指令。
- 呈現(xiàn)引擎 - 負(fù)責(zé)顯示請(qǐng)求的內(nèi)容。如果請(qǐng)求的內(nèi)容是 HTML,它就負(fù)責(zé)解析 HTML 和 CSS 內(nèi)容,并將解析后的內(nèi)容顯示在屏幕上。
- ?絡(luò) - ?于?絡(luò)調(diào)?,?如 HTTP 請(qǐng)求。其接?與平臺(tái)?關(guān),并為所有平臺(tái)提供底層實(shí)現(xiàn)。
- ?戶界?后端 - ?于繪制基本的窗??部件,?如組合框和窗?。其公開(kāi)了與平臺(tái)?關(guān)的通?接?,?在底層使?操作系統(tǒng)的?戶界??法。
- JavaScript 解釋器。?于解析和執(zhí)? JavaScript 代碼。
- 數(shù)據(jù)存儲(chǔ) - 這是持久層。瀏覽器需要在硬盤上保存各種數(shù)據(jù),例如 Cookie。新的 HTML 規(guī)范 (HTML5) 定義了“?絡(luò)數(shù)據(jù)庫(kù)”,這是?個(gè)完整(但是輕便)的瀏覽器內(nèi)數(shù)據(jù)庫(kù)。
值得注意的是,和?多數(shù)瀏覽器不同,Chrome 瀏覽器的每個(gè)標(biāo)簽?都分別對(duì)應(yīng)?個(gè)呈現(xiàn)引擎實(shí)例。每個(gè)標(biāo)簽?都是?個(gè)獨(dú)?的進(jìn)程。
二、瀏覽器渲染原理
1.瀏覽器的渲染過(guò)程
瀏覽器渲染主要有以下步驟:
步驟一:
- 首先解析收到的文檔,根據(jù)文檔定義構(gòu)建一棵 DOM 樹(shù),DOM 樹(shù)是由 DOM 元素及屬性節(jié)點(diǎn)組成的。
步驟二:
- 然后對(duì) CSS 進(jìn)行解析,生成 CSSOM 規(guī)則樹(shù)。
? 1、把css轉(zhuǎn)換為瀏覽器內(nèi)容理解的結(jié)構(gòu)
? 當(dāng)渲染引擎接收到CSS文本的時(shí),會(huì)執(zhí)行一個(gè)轉(zhuǎn)換操作,將css文本轉(zhuǎn)換為瀏覽器可以理解的結(jié)構(gòu)–styleSheets。
? 2、轉(zhuǎn)換樣式表中的屬性值,使其標(biāo)準(zhǔn)化
? 什么是標(biāo)準(zhǔn)化:
body { font-size: 2em } p {color:blue;} span {display: none} div {font-weight: bold} div p {color:green;} div {color:red; }可以看到上面的 CSS 文本中有很多屬性值,如 2em、blue、bold,這些類型數(shù)值不容易被渲染引擎理解,所以需要將所有值轉(zhuǎn)換為渲染引擎容易理解的、標(biāo)準(zhǔn)化的計(jì)算值,這個(gè)過(guò)程就是屬性值標(biāo)準(zhǔn)化。
? 3、計(jì)算出DOM樹(shù)中每一個(gè)節(jié)點(diǎn)的具體樣式
? 涉及到CSS的繼承規(guī)則和層疊規(guī)則。
? 樣式計(jì)算階段的目的是為了計(jì)算出 DOM 節(jié)點(diǎn)中每個(gè)元素的具體樣式,在計(jì)算過(guò)程中需要遵守 CSS 的繼承和層疊兩個(gè)規(guī)則。這個(gè)階段最終輸出的內(nèi)容是每個(gè) DOM 節(jié)點(diǎn)的樣式,并被保存在 ComputedStyle 的結(jié)構(gòu)內(nèi)。
步驟三:
- 根據(jù) DOM 樹(shù)和 CSSOM 規(guī)則樹(shù)構(gòu)建渲染樹(shù)。渲染樹(shù)的節(jié)點(diǎn)被稱為渲染對(duì)象,渲染對(duì)象是一個(gè)包含有顏色和大小等屬性的矩形,渲染對(duì)象和 DOM 元素相對(duì)應(yīng),但這種對(duì)應(yīng)關(guān)系不是一對(duì)一的,不可見(jiàn)的 DOM 元素不會(huì)被插入渲染樹(shù)。還有一些 DOM元素對(duì)應(yīng)幾個(gè)可見(jiàn)對(duì)象,它們一般是一些具有復(fù)雜結(jié)構(gòu)的元素,無(wú)法用一個(gè)矩形來(lái)描述。
步驟四:
- 當(dāng)渲染對(duì)象被創(chuàng)建并添加到樹(shù)中,它們并沒(méi)有位置和大小,所以當(dāng)瀏覽器生成渲染樹(shù)以后,就會(huì)根據(jù)渲染樹(shù)來(lái)進(jìn)行布局(也可以叫做回流)。這一階段瀏覽器要做的事情是要弄清楚各個(gè)節(jié)點(diǎn)在頁(yè)面中的確切位置和大小。通常這一行為也被稱為“自動(dòng)重排”。
詳細(xì)過(guò)程:
因?yàn)轫?yè)面中有很多復(fù)雜的效果,如一些復(fù)雜的3D轉(zhuǎn)換,頁(yè)面滾動(dòng),或者使用z-index,為了更方便的實(shí)現(xiàn)這些效果,渲染引擎還需要為特定的節(jié)點(diǎn)生成專門的圖層,并生成一棵對(duì)應(yīng)的圖層樹(shù)(LayerTree)。這和PS的圖層類似,正是這些圖層疊加在一起才最終構(gòu)成了頁(yè)面圖像。
實(shí)際上,瀏覽器的頁(yè)面被分成了很多圖層,這些圖層疊加在一起后,最終合成了頁(yè)面。
布局樹(shù)和圖層樹(shù)的關(guān)系
通常情況下,并不是布局樹(shù)中的每一個(gè)節(jié)點(diǎn)都包含一個(gè)圖層,如果一個(gè)節(jié)點(diǎn)沒(méi)有對(duì)應(yīng)的圖層,那么這個(gè)節(jié)點(diǎn)就從屬于父節(jié)點(diǎn)的圖層。那么什么情況滿足,渲染引擎才會(huì)為特定的節(jié)點(diǎn)創(chuàng)建新的圖層呢?滿足一下兩個(gè)條件中的任意一個(gè),元素就可以被單獨(dú)提升為一個(gè)圖層。
1、擁有層疊上下文屬性的元素會(huì)被提升為單獨(dú)的一層。
頁(yè)面是一個(gè)二維平面,但層疊上下文能夠上HTML元素?fù)碛腥S概念,這些HTML元素按自身屬性的優(yōu)先級(jí)分布在垂直于這個(gè)二維平面的Z軸上,以下情況會(huì)作為單獨(dú)的圖層
1、position:fixed 2、css 3d 例如:transform:rotateX(30deg) 3、video 4、canvas 5、有css3動(dòng)畫的節(jié)點(diǎn)2、需要剪裁的地方也會(huì)被創(chuàng)建為圖層
文字內(nèi)容溢出div的情況
把div的大小限定為200200像素,而div里面的文字內(nèi)容比較多,文字所顯示的區(qū)域肯定會(huì)超過(guò)200200的面積,這時(shí)候就產(chǎn)生了剪裁,渲染引擎會(huì)把裁剪文字內(nèi)容的一部分用于顯示在div區(qū)域,下面是運(yùn)行時(shí)的執(zhí)行結(jié)果:
出現(xiàn)這種裁剪情況時(shí),渲染引擎會(huì)為文字單獨(dú)為文字創(chuàng)建一層,如出現(xiàn)滾動(dòng)條,滾動(dòng)條也會(huì)被提升為單獨(dú)的層。
步驟五:
- 布局階段結(jié)束后是繪制階段,遍歷渲染樹(shù)并調(diào)用渲染對(duì)象的 paint 方法將它們的內(nèi)容顯示在屏幕上,繪制使用 UI 基礎(chǔ)組件。
**注意:**這個(gè)過(guò)程是逐步完成的,為了更好的用戶體驗(yàn),渲染引擎將會(huì)盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會(huì)等到所有的html 都解析完成之后再去構(gòu)建和布局 render 樹(shù)。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時(shí),可能還在通過(guò)網(wǎng)絡(luò)下載其余內(nèi)容。
2.相關(guān)概念
“重排”,“重繪"和"合成”。
①重排(更新元素的幾何屬性)
如果你通過(guò)JS或css修改元素的幾何位置屬性,如width,height等,那么會(huì)觸發(fā)瀏覽器的重新布局,解析之后的一系列子階段,這個(gè)過(guò)程就叫重排。重排需要更新完整的渲染流水線,所以開(kāi)銷也最大的。
②重繪(更細(xì)元素的繪制屬性)
修改元素的背景色,布局階段不會(huì)執(zhí)行,因?yàn)?strong>沒(méi)有引起幾何位置的變換,所以直接進(jìn)入繪制,然后執(zhí)行之后的一系列子階段,這個(gè)過(guò)程就叫重繪。相較重排操作,重繪省去了布局和分層階段,所以執(zhí)行效率會(huì)比重排效率高。
③直接合成階段
那如果你更改一個(gè)既不要布局也不要繪制的屬性,渲染引擎將跳過(guò)布局和繪制,只執(zhí)行后續(xù)的合成操作,我們把這個(gè)過(guò)程叫做合成。
使用CSS的transform來(lái)實(shí)現(xiàn)動(dòng)畫效果,可以避開(kāi)重排和重繪階段,直接在非主線程上執(zhí)行合成動(dòng)畫操作。這樣的效率最高,因?yàn)槭窃诜侵骶€程上合成的,并沒(méi)有占用主線程的資源。
三.瀏覽器渲染優(yōu)化
(1)針對(duì)JavaScript:
JavaScript既會(huì)阻塞HTML的解析,也會(huì)阻塞CSS的解析。因此我們可以對(duì)JavaScript的加載方式進(jìn)行改變,來(lái)進(jìn)行優(yōu)化:
(1)盡量將JavaScript文件放在body的最后
(2) body中間盡量不要寫<script>標(biāo)簽
(3)<script>標(biāo)簽的引入資源方式有三種,有一種就是我們常用的直接引入,還有兩種就是使用 async 屬性和 defer 屬性來(lái)異步引入,兩者都是去異步加載外部的JS文件,不會(huì)阻塞DOM的解析(盡量使用異步加載)。三者的區(qū)別如下:
- script 立即停止頁(yè)面渲染去加載資源文件,當(dāng)資源加載完畢后立即執(zhí)行js代碼,js代碼執(zhí)行完畢后繼續(xù)渲染頁(yè)面;
- async 是在下載完成之后,立即異步加載,加載好后立即執(zhí)行,多個(gè)帶async屬性的標(biāo)簽,不能保證加載的順序;
- defer 是在下載完成之后,立即異步加載。加載好后,如果 DOM 樹(shù)還沒(méi)構(gòu)建好,則先等 DOM 樹(shù)解析好再執(zhí)行;如果DOM樹(shù)已經(jīng)準(zhǔn)備好,則立即執(zhí)行。多個(gè)帶defer屬性的標(biāo)簽,按照順序執(zhí)行。
(2)針對(duì)CSS:
css要放在頂部,它不會(huì)阻塞html的解析
使用CSS有三種方式:使用link、@import、內(nèi)聯(lián)樣式,其中l(wèi)ink和@import都是導(dǎo)入外部樣式。它們之間的區(qū)別:
- link:瀏覽器會(huì)派發(fā)一個(gè)新的線程(HTTP線程)去加載資源文件,與此同時(shí)GUI渲染線程會(huì)繼續(xù)向下渲染代碼
- @import:GUI渲染線程會(huì)暫時(shí)停止渲染,去服務(wù)器加載資源文件,資源文件沒(méi)有返回之前不會(huì)繼續(xù)渲染(阻礙瀏覽器渲染)
- style:GUI直接渲染
外部樣式如果長(zhǎng)時(shí)間沒(méi)有加載完畢,瀏覽器為了用戶體驗(yàn),會(huì)使用瀏覽器會(huì)默認(rèn)樣式,確保首次渲染的速度。所以CSS一般寫在headr中,讓瀏覽器盡快發(fā)送請(qǐng)求去獲取css樣式。
所以,在開(kāi)發(fā)過(guò)程中,導(dǎo)入外部樣式使用link,而不用@import。如果css少,盡可能采用內(nèi)嵌樣式,直接寫在style標(biāo)簽中。
style樣式解析
瀏覽器一次是加載64kb,現(xiàn)在我們加載的頁(yè)面過(guò)大,所有會(huì)分段接收。
Tips: style中的樣式不解析完畢,頁(yè)面不會(huì)渲染。
1、由html解析器解析
2、不會(huì)阻塞瀏覽器渲染,但可能出現(xiàn)閃屏現(xiàn)象
3、不會(huì)阻塞DOM解析
通過(guò)style標(biāo)簽引入會(huì)一般解析,一邊渲染。
link引入
1、link進(jìn)來(lái)的樣式,由css解析器去解析,并且是同步解析的。
2、css解析器會(huì)阻塞頁(yè)面的渲染。(link引入的外部樣式會(huì)阻塞頁(yè)面渲染)
? (可以利用這種阻塞避免"閃屏現(xiàn)象")
3、推薦使用link引入樣式。
4.阻塞后面的js語(yǔ)句的執(zhí)行
(3)針對(duì)DOM樹(shù)、CSSOM樹(shù):
可以通過(guò)以下幾種方式來(lái)減少渲染的時(shí)間:
- HTML文件的代碼層級(jí)盡量不要太深
- 使用語(yǔ)義化的標(biāo)簽,來(lái)避免不標(biāo)準(zhǔn)語(yǔ)義化的特殊處理
- 減少CSSD代碼的層級(jí),因?yàn)檫x擇器是從左向右進(jìn)行解析的
(4)減少回流與重繪:
- 操作DOM時(shí),盡量在低層級(jí)的DOM節(jié)點(diǎn)進(jìn)行操作
- 不要使用table布局, 一個(gè)小的改動(dòng)可能會(huì)使整個(gè)table進(jìn)行重新布局
- 使用CSS的表達(dá)式
- 不要頻繁操作元素的樣式,對(duì)于靜態(tài)頁(yè)面,可以修改類名,而不是樣式。
- 使用absolute或者fixed,使元素脫離文檔流,這樣他們發(fā)生變化就不會(huì)影響其他元素
- 避免頻繁操作DOM,可以創(chuàng)建一個(gè)文檔片段documentFragment,在它上面應(yīng)用所有DOM操作,最后再把它添加到文檔中
- 將元素先設(shè)置display: none,操作結(jié)束后再把它顯示出來(lái)。因?yàn)樵赿isplay屬性為none的元素上進(jìn)行的DOM操作不會(huì)引發(fā)回流和重繪。
- 將DOM的多個(gè)讀操作(或者寫操作)放在一起,而不是讀寫操作穿插著寫。這得益于瀏覽器的渲染隊(duì)列機(jī)制。
瀏覽器針對(duì)頁(yè)面的回流與重繪,進(jìn)行了自身的優(yōu)化——渲染隊(duì)列
瀏覽器會(huì)將所有的回流、重繪的操作放在一個(gè)隊(duì)列中,當(dāng)隊(duì)列中的操作到了一定的數(shù)量或者到了一定的時(shí)間間隔,瀏覽器就會(huì)對(duì)隊(duì)列進(jìn)行批處理。這樣就會(huì)讓多次的回流、重繪變成一次回流重繪。
將多個(gè)讀操作(或者寫操作)放在一起,就會(huì)等所有的讀操作進(jìn)入隊(duì)列之后執(zhí)行,這樣,原本應(yīng)該是觸發(fā)多次回流,變成了只觸發(fā)一次回流。
總結(jié)
以上是生活随笔為你收集整理的前端浏览器渲染原理及优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ios 输入法扩展_动态输入法 iOS
- 下一篇: 2017年html5行业报告,云适配发布