一.什么是頁面的重繪與回流
瀏覽器在渲染一個(gè)頁面的時(shí)候,從加載到完成柠座,首先是構(gòu)建DOM樹片橡,然后根據(jù)DOM節(jié)點(diǎn)的幾何屬性生成渲染樹(不包括display:none,head節(jié)點(diǎn)但是會(huì)包括visibility:hidden節(jié)點(diǎn)),當(dāng)渲染樹構(gòu)建完成吹泡,頁面就根據(jù)DOM樹開始布局了经瓷,渲染樹也根據(jù)設(shè)置的樣式對(duì)應(yīng)的渲染這些節(jié)點(diǎn)。在這個(gè)過程中揭朝,回流與dom樹和渲染樹有關(guān),重繪與渲染樹有關(guān)潭袱。
比如我們刪除一個(gè)dom節(jié)點(diǎn),修改一個(gè)元素的寬高编丘,這樣就會(huì)導(dǎo)致頁面的布局發(fā)生變化彤悔,DOM樹的結(jié)構(gòu)發(fā)生變化,引起dom樹的重構(gòu)晕窑,重構(gòu)完成之后就會(huì)導(dǎo)致渲染樹的重新渲染,這個(gè)過程就叫做回流
當(dāng)我們修改一個(gè)元素的顏色蓝丙,這并不會(huì)影響頁面的布局望拖,但是渲染樹會(huì)重新渲染頁面的樣式顏色,這就是重繪
回流的代價(jià)是遠(yuǎn)遠(yuǎn)大于重繪的鸥跟,回流一定導(dǎo)致重繪盔沫,但是重繪不一定導(dǎo)致回流。
二.常見的場景
回流常見于元素的尺寸架诞,布局,隱藏等Dom結(jié)構(gòu)發(fā)生改變的情況
1.添加或者刪除可見的dom元素
2.元素位置改變
3.元素尺寸改變(邊距很泊,填充沾谓,邊框,高度和寬度)
4.內(nèi)容改變(內(nèi)容物引起的元素大小發(fā)生變化)
5.頁面渲染初始化
6.瀏覽器尺寸改變
7.計(jì)算元素的偏移量屬性(瀏覽器為了確保屬性值的正確性會(huì)回流得到最新值昏兆,所以最好使用一個(gè)變量記錄一下)
重繪常見于元素的顏色的樣式發(fā)生改變的情況
1.改變字體
2.增加或者移除樣式表
3.內(nèi)容變化(input輸入框)
4.激活CSS偽類
5.設(shè)置style屬性值
6.計(jì)算offsetWidth和offsetHeight屬性
三.如何優(yōu)化瀏覽器的回流與重繪
1.將那些改變樣式的操作集合在一次完事妇穴,直接改變className或者cssText
- 使用cssText
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
- 修改CSS的class
const el = document.getElementById('test');
el.className += ' active';
2.讓要操作的元素進(jìn)行離線處理隶债,處理完事以后再一起更新
(1)使用DocumentFragment進(jìn)行緩存操作饮潦,引發(fā)一次回流和重繪
(2)使用display:none,只引發(fā)兩次回流和重繪。道理跟上面的一樣回俐。因?yàn)?strong>display:none的元素不會(huì)出現(xiàn)在render樹
function appendDataToElement(appendToElement, data) {
let li;
for (let i = 0; i < data.length; i++) {
li = document.createElement('li');
li.textContent = 'text';
appendToElement.appendChild(li);
}
}
const ul = document.getElementById('list');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';
(3)使用cloneNode和replaceChild技術(shù)稀并,引發(fā)一次回流和重繪(將原始元素拷貝到一個(gè)脫離文檔流的節(jié)點(diǎn)中,修改節(jié)點(diǎn)之后忘瓦,再替換原始元素)
const ul = document.getElementById('list');
const clone = ul.cloneNode(true);
appendDataToElement(clone, data);
ul.parentNode.replaceChild(clone, ul);
3.不要經(jīng)常訪問會(huì)引起瀏覽器flush隊(duì)列的屬性引颈,非要高頻訪問的話建議緩存到變量;
4.將需要多次重排的元素凌停,position屬性設(shè)為absolute或fixed售滤,這樣此元素就脫離了文檔流,它的變化不會(huì)影響到其他元素完箩。例如有動(dòng)畫效果的元素就最好設(shè)置為絕對(duì)定位;
5.盡量不要使用表格布局阻逮,如果沒有定寬秩彤,表格一列的寬度由最寬的一列決定,那么很可能在最后一行的寬度超出之前的列寬,引起整體回流造成table可能需要多次計(jì)算才能確定好其在渲染樹中節(jié)點(diǎn)的屬性慷蠕,通常要花3倍于同等元素的時(shí)間。
6.避免觸發(fā)同步布局事件
現(xiàn)代瀏覽器都會(huì)通過隊(duì)列化修改并批量執(zhí)行來優(yōu)化重排過程澎现。瀏覽器會(huì)將修改操作放入到隊(duì)列里,直到過了一段時(shí)間或者操作達(dá)到了一個(gè)閾值剑辫,才清空隊(duì)列。但是椎眯!當(dāng)你獲取布局信息的操作的時(shí)候胳岂,會(huì)強(qiáng)制隊(duì)列刷新,比如當(dāng)你訪問以下屬性或者使用以下方法:
- offsetTop掌测、offsetLeft产园、offsetWidth、offsetHeight
- scrollTop什燕、scrollLeft、scrollWidth仲义、scrollHeight
- clientTop剑勾、clientLeft、clientWidth虽另、clientHeight
- getComputedStyle()
- getBoundingClientRect
function initP() {
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
改為
const width = box.offsetWidth;
function initP() {
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = width + 'px';
}
}
以上屬性和方法都需要返回最新的布局信息捂刺,因此瀏覽器不得不清空隊(duì)列,觸發(fā)回流重繪來返回正確的值族展。因此,我們?cè)谛薷臉邮降臅r(shí)候贵涵,最好避免使用上面列出的屬性,他們都會(huì)刷新渲染隊(duì)列宾茂。如果要使用它們,最好將值緩存起來
7.css3硬件加速欧聘,使用部分css3的屬性不會(huì)引發(fā)頁面的回流與重繪或者造成的影響比較小
四.瀏覽器渲染的過程
渲染過程大致如下:
1.解析HTML怀骤,生成DOM樹爱谁,解析CSS,生成CSSOM樹
2.將DOM樹和CSSOM樹結(jié)合访敌,生成渲染樹
3.回流(Layout):根據(jù)生成的渲染樹,進(jìn)行回流得到節(jié)點(diǎn)信息(位置爷抓,大凶杷堋)
4.重繪(Painting):根據(jù)渲染樹以及回流得到的幾何信息,得到節(jié)點(diǎn)的絕對(duì)像素
5.Display:將像素發(fā)送給GPU陈莽,展示在頁面上
生成渲染樹
1.從DOM樹的根節(jié)點(diǎn)開始遍歷每個(gè)可見節(jié)點(diǎn)。
2.對(duì)于每個(gè)可見的節(jié)點(diǎn)独柑,找到CSSOM樹中對(duì)應(yīng)的規(guī)則升酣,并應(yīng)用它們泳唠。
3.根據(jù)每個(gè)可見節(jié)點(diǎn)以及其對(duì)應(yīng)的樣式渠羞,組合生成渲染樹漠其。
不可見的節(jié)點(diǎn):(渲染樹只包含可見的節(jié)點(diǎn))
- 一些不會(huì)被渲染出來的點(diǎn)瑞驱,比如:script窄坦,meta晰筛,link等
- 一些通過css進(jìn)行隱藏的節(jié)點(diǎn)拴袭。比如display:none曙博。注意,利用visibility和opacity隱藏的節(jié)點(diǎn)般哼,還是會(huì)顯示在渲染樹上的惠窄。只有display:none的節(jié)點(diǎn)才不會(huì)顯示在渲染樹上。
學(xué)習(xí)鏈接:
1.https://www.cnblogs.com/dujingjie/p/5784890.html
2.https://www.cnblogs.com/wanan-01/p/7732340.html
3.https://zhuanlan.zhihu.com/p/22181897
4.https://zhuanlan.zhihu.com/p/52076790