學習渡一課程、參考 必須明白的瀏覽器渲染機制 - 掘金
渲染機制的流程
HTML解析
布局
分層
繪制
分塊
光柵化
畫
HTML解析 - Parse HTML
解析html會生成一個 dom樹和cssom樹
?
document.styleSheets? 可以看到cssom樹?
?
渲染阻塞
在渲染的過程中,遇到一個script標記時,就會停止渲染,去請求腳本文件并執(zhí)行腳本文件,因為瀏覽器渲染和 JS 執(zhí)行共用一個線程,而且這里必須是單線程操作,多線程會產(chǎn)生渲染 DOM 沖突。JavaScript的加載、解析與執(zhí)行會嚴重阻塞DOM的構(gòu)建。只有等到腳本文件執(zhí)行完畢,才會去繼續(xù)構(gòu)建DOM。
js不單會阻塞DOM構(gòu)建,還會導致CSSOM也阻塞DOM的構(gòu)建,如果JavaScript腳本還操作了CSSOM,而正好這個CSSOM還沒有下載和構(gòu)建,瀏覽器甚至會延遲腳本執(zhí)行和構(gòu)建DOM,直至完成其CSSOM的下載和構(gòu)建,然后再執(zhí)行JavaScript,最后在繼續(xù)構(gòu)建DOM
因此script的位置很重要,在實際使用過程中遵循以下兩個原則:
- CSS 優(yōu)先:引入順序上,CSS 資源先于 JavaScript 資源。
- JS置后:我們通常把JS代碼放到頁面底部,且JavaScript 應盡量少影響 DOM 的構(gòu)建
渲染
當我們生成DOM樹和CSSOM樹后,我們需要將這兩顆樹合并成渲染樹,渲染步驟包括樣式、布局、繪制
樣式計算
主線程會遍歷得到DOM樹,依次為樹中每個節(jié)點計算出它最終的樣式,稱之為Computed Style
這一過程中,很多預設值會變成絕對值,比如 red會變成rgb(255,0,0)相對單位會變成覺得單位 em 變成px。
布局
瀏覽器拿到渲染樹后,就會從渲染樹的根節(jié)點開始遍歷,然后確定每個節(jié)點對象在頁面上的確切大小與位置,通常這一行為也被稱為“自動重排”。布局階段的輸出是一個盒子模型,它會精確地捕獲每個元素在屏幕內(nèi)的確切位置與大小,所有相對測量值都將轉(zhuǎn)換為屏幕上的絕對像素。這一過程也可稱為回流。
?分層
分層是占用內(nèi)存空間的,主線程會使用一套復雜的策略對整個布局樹進行分層,分層的好處在于,將來某一個層改變后,僅會對該層進行后續(xù)處理,重而提升效率。
?繪制和光柵化
在繪制或光柵化階段,瀏覽器將在布局階段計算的每個框轉(zhuǎn)換為屏幕上的實際像素。繪畫包括將元素的每個可視部分繪制到屏幕上,包括文本、顏色、邊框、陰影和替換的元素(如按鈕和圖像)。瀏覽器需要非??斓赝瓿蛇@項工作。
性能優(yōu)化策略
在我們了解瀏覽器的渲染機制后,DOM 和 CSSOM 結(jié)構(gòu)構(gòu)建順序,我們可以針對性能優(yōu)化問題給出一些方案,提升頁面性能。
回流(reflow)與重繪(repaint)
當元素的樣式發(fā)生變化時,瀏覽器需要觸發(fā)更新,重新繪制元素。這個過程中,有兩種類型的操作,即重繪與回流。
- 重繪(repaint): 當元素樣式的改變不影響布局時,瀏覽器將使用重繪對元素進行更新,此時由于只需要UI層面的重新像素繪制,因此損耗較少
- 回流(reflow): 當元素的尺寸、結(jié)構(gòu)或觸發(fā)某些屬性時,瀏覽器會重新渲染頁面,稱為回流。此時,瀏覽器需要重新經(jīng)過計算,計算后還需要重新頁面布局,因此是較重的操作。會觸發(fā)回流的操作:
- 添加或刪除可見的DOM元素
- 元素的位置發(fā)生變化
- 元素的尺寸發(fā)生變化(包括外邊距、內(nèi)邊框、邊框大小、高度和寬度等)
- 內(nèi)容發(fā)生變化,比如文本變化或圖片被另一個不同尺寸的圖片所替代。
- 頁面一開始渲染的時候(這肯定避免不了)
- 瀏覽器的窗口尺寸變化(因為回流是根據(jù)視口的大小來計算元素的位置和大小的
注意:回流一定會觸發(fā)重繪,而重繪不一定會回流,重繪的開銷較小,回流的代價較高
因此為了減少性能優(yōu)化,我們可以盡量避免回流或者重繪操作 css
- 避免使用table布局
- 將動畫效果應用到position屬性為absolute或fixed的元素上
javascript
- 避免頻繁操作樣式,可匯總后統(tǒng)一 一次修改
- 盡量使用class進行樣式修改
- 減少dom的增刪次數(shù),可使用 字符串 或者 documentFragment 一次性插入
- 極限優(yōu)化時,修改樣式可將其display: none后修改
- 避免多次觸發(fā)上面提到的那些會觸發(fā)回流的方法,可以的話盡量用 變量存住
async和defer的作用是什么?有什么區(qū)別?
defer 和 async 屬性的區(qū)別:
其中藍色線代表JavaScript加載;紅色線代表JavaScript執(zhí)行;綠色線代表 HTML 解析 1)情況1 <scriptsrc="script.js"> 沒有 defer 或 async,瀏覽器會立即加載并執(zhí)行指定的腳本,也就是說不等待后續(xù)載入的文檔元素,讀到就加載并執(zhí)行。
2)情況2 (異步下載) async 屬性表示異步執(zhí)行引入的 JavaScript,與 defer 的區(qū)別在于,如果已經(jīng)加載好,就會開始執(zhí)行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發(fā)之后。需要注意的是,這種方式加載的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發(fā)之前或之后執(zhí)行,但一定在 load 觸發(fā)之前執(zhí)行。
3)情況3 <scriptdefersrc="script.js">(延遲執(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是有順序的加載
js優(yōu)化可以在script標簽加上 defer屬性 和 async屬性用于在不阻塞頁面文檔解析的前提下,控制腳本的下載和執(zhí)行文章來源:http://www.zghlxwxcb.cn/news/detail-682048.html
其他: CSS 標簽的 rel屬性 中的屬性值設置為 preload 能夠讓你在你的HTML頁面中可以指明哪些資源是在頁面加載完成后即刻需要的,最優(yōu)的配置加載順序,提高渲染性能文章來源地址http://www.zghlxwxcb.cn/news/detail-682048.html
首屏優(yōu)化加載
- 減少首屏CGI的計算量:比如在微信8.8無現(xiàn)金日H5開發(fā)中,前端希望拿到用戶的個人信息、消費記錄、排名三類數(shù)據(jù),如果只通過一個CGI來處理,那么后臺響應時間肯定會變長;由于在H5的首屏中,只包含了用戶信息,消費記錄、排名都在第2屏和第3屏,此時其實可以利用異步的方式來拿消費記錄、排名的數(shù)據(jù)。
- 頁面瘦身:壓縮HTML、CSS、JavaScript。
- 減少請求:CSS、JavaScript文件數(shù)盡量少,甚至當CSS、JS的代碼不多時,可以考慮直接將代碼內(nèi)嵌到頁面中。
- 多用緩存:緩存能大幅度降低頁面非首次加載的時間。
- 少用table布局,瀏覽器在渲染table時會消耗較多資源,而且只有table里有一點變化,整個table都會重新渲染。
- 做預加載:部分H5頁面首屏可能要下載較多的靜態(tài)資源,比如圖片,這時為了避免加載時出現(xiàn)“難看”的頁面,用預加載(loading的方式)做一個過渡
到了這里,關于瀏覽器渲染機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!