理解瀏覽器重繪和回流
大家好,我是前端西瓜哥。今天帶大家理解瀏覽器的重繪和回流。
瀏覽器渲染過程
我們先簡(jiǎn)單了解一些瀏覽器是怎么渲染頁面的。
進(jìn)入頁面時(shí)會(huì)請(qǐng)求一個(gè) HTML,HTML 會(huì)被解析為 DOM(文檔對(duì)象模型) 樹,其根節(jié)點(diǎn)為 documentElement,也就是 <html>。
然后請(qǐng)求 CSS (層疊樣式表)文件。
CSS 來源很豐富,有瀏覽器自己的兜底樣式(User-Agent stylesheets)、通過 link 或 @import 導(dǎo)入的各種外部樣式、style 寫的內(nèi)嵌樣式、以及在標(biāo)簽上基于 style 屬性的內(nèi)聯(lián)樣式。
加載完后就會(huì)解析它們,會(huì)根據(jù)繼承規(guī)則、層疊規(guī)則合并成一個(gè) CSSOM 樹,該樹表示特定選擇器嵌套下的最終樣式。
最后將 DOM 和 CSSOM 組合,生成渲染樹(render)。
渲染樹會(huì)將不可見標(biāo)簽丟棄掉,比如像 <head> 僅承載信息不表示結(jié)構(gòu)的標(biāo)簽,或是設(shè)置了 display: none 的元素。這里的每個(gè)節(jié)點(diǎn)都是一個(gè)盒子(box),應(yīng)用盒子模型,有它們各自的 width、padding、margin 等元素。
渲染樹生成后,先是會(huì)計(jì)算 “布局”,然后分層,最后進(jìn)行柵格化(渲染)。
重繪(repaint)
重繪,就是重新繪制。發(fā)生了不改變?cè)匚锢硇畔⒌那闆r下只會(huì)進(jìn)行重繪。比如將元素的背景色修改了,就要將元素的盒子做一個(gè)重新渲染。
重繪不會(huì)改變頁面的布局,只是對(duì)局部區(qū)域重新渲染,一般來說不會(huì)導(dǎo)致嚴(yán)重的性能問題。
重排(reflow)
重排,就是重新排布。
當(dāng)元素的物理信息發(fā)生變化時(shí),其后的元素就會(huì)改變位置,此時(shí)就要重新進(jìn)行布局,計(jì)算元素的物理信息。
比如修改元素的高度,將元素設(shè)置為 display: none; 等操作會(huì)導(dǎo)致重排。
下面是維基百科的頁面重排的可視化展示:
如何避免重繪重排
- 減少 DOM 操作。像是 Vue 和 React 通過虛擬 DOM 找出不同,以減少更新 DOM 的操作;
- 盡量將要添加的元素都生成好,再一次性添加到文檔流中,而不是一個(gè)個(gè)加上去;
- 緩存好要用的布局信息。對(duì)于同時(shí)調(diào)用多次修改 DOM 的 API 操作,瀏覽器是有優(yōu)化的,會(huì)將這些操作緩存起來,然后一次性更新。但如果在這過程中訪問了布局相關(guān)信息(比如 scrollHeight、getBoundingClientRect)時(shí),就會(huì)強(qiáng)制進(jìn)行重渲染去獲取最新布局?jǐn)?shù)據(jù);
- 將經(jīng)常變化的元素放到新的層。通過 transform 或絕對(duì)定位產(chǎn)生新的渲染層,防止影響文檔流;
結(jié)尾
平時(shí)我們應(yīng)該盡量避免頻繁一次性修改大量 DOM。好在我們平常使用類似 Vue 和 React 的框架,能夠通過對(duì)比新舊虛擬 DOM 減少對(duì)真實(shí) DOM 的操作,將 GPU 密集轉(zhuǎn)移到了 CPU 密集,也算各有利弊吧。