摘要:合并樣式操作比如可以合并成批量修改使元素脫離文檔流,再對(duì)其進(jìn)行操作,然后再把元素帶回文檔中,這種辦法可以有效減少重繪重排的次數(shù)。
前言
現(xiàn)代web框架大多都是數(shù)據(jù)驅(qū)動(dòng)類的,比如 react, vue,所以開發(fā)者不需要直接接觸 DOM,修改 data 便可以驅(qū)動(dòng)界面更新。但是作為前端工程師,了解瀏覽器的重繪與重排還是很有必要的,可以幫助我們寫出更好性能的 web 應(yīng)用。
瀏覽器的渲染CSS Tree: 瀏覽器將 CSS 解析成 CSSOM 的樹形結(jié)構(gòu)
DOM Tree:瀏覽器將 HTML 解析成樹形的數(shù)據(jù)結(jié)構(gòu)
Render Tree:將 DOM 與 CSSOM 合并成一個(gè)渲染樹
有了渲染樹(Render Tree),瀏覽器就知道網(wǎng)頁(yè)中有哪些節(jié)點(diǎn),以及各個(gè)節(jié)點(diǎn)與 CSS 的關(guān)系,從而知道每個(gè)節(jié)點(diǎn)的位置和幾何屬性,然后繪制頁(yè)面。
重繪與重排當(dāng) DOM 的變化影響了元素的幾何屬性(比如 width 和 height ),就會(huì)導(dǎo)致瀏覽器重新計(jì)算元素的幾何屬性,同樣受到該元素影響的其他元素也會(huì)發(fā)生重新計(jì)算。此時(shí),瀏覽器會(huì)使渲染樹中受到影響的部分失效,并重新構(gòu)造渲染樹。這個(gè)過程被稱為重排(也叫“回流”)(reflow),完成重排之后,瀏覽器會(huì)重新繪制受影響的部分到頁(yè)面上,這個(gè)過程就是重繪(repaint)。
所以重排一定會(huì)引起重繪,而重繪不一定會(huì)引起重排,比如一個(gè)元素的改變并沒有影響布局的改變(background-color的改變),在這種情況下,只會(huì)發(fā)生一個(gè)重繪(不需要重排)。
引起重排的因素可以總結(jié)出,當(dāng)元素的幾何屬性或頁(yè)面布局發(fā)生改變就會(huì)引起重排,比如:
對(duì)可見 DOM 元素的操作(添加,刪除或順序變化)
元素位置發(fā)生改變
元素的幾何屬性發(fā)生改變(比如:外邊距、內(nèi)邊距、邊框?qū)挾纫约皟?nèi)容改變引起的寬高的改變)
頁(yè)面首次渲染
偽類樣式激活(hover等)
瀏覽器視口尺寸發(fā)生改變(滾動(dòng)或縮放)
如何優(yōu)化重繪與重排都是代價(jià)昂貴的操作(因?yàn)槊看沃嘏哦紩?huì)產(chǎn)生計(jì)算消耗),它們會(huì)導(dǎo)致 web 應(yīng)用的 UI 反應(yīng)遲鈍,所以開發(fā)者在編寫應(yīng)用程序的時(shí)候應(yīng)當(dāng)盡量減少這類過程的發(fā)生。
渲染樹隊(duì)列因?yàn)檫^多的重繪與重排可能會(huì)導(dǎo)致應(yīng)用的卡頓,所以瀏覽器會(huì)對(duì)這個(gè)有一個(gè)優(yōu)化的過程。大多數(shù)瀏覽器會(huì)通過隊(duì)列化來批量執(zhí)行(比如把腳本對(duì) DOM 的修改放入一個(gè)隊(duì)列,在隊(duì)列所有操作都結(jié)束后再進(jìn)行一次繪制)。但是開發(fā)者有時(shí)可能不知不覺的強(qiáng)制刷新渲染隊(duì)列來立即進(jìn)行重排重繪,比如獲取頁(yè)面布局信息會(huì)導(dǎo)致渲染隊(duì)列的強(qiáng)制刷新,以下屬性或方法會(huì)立即觸發(fā)頁(yè)面繪制:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()
以上屬性和方法都是要瀏覽器返回最新的布局信息,所以瀏覽器會(huì)立刻執(zhí)行渲染隊(duì)列中的“待處理變化”, 并觸發(fā)重排重繪然后返回最新的值。所以在修改樣式的過程中,應(yīng)該盡量避免使用以上屬性和方法。
減少重繪與重排為了減少重繪重排的發(fā)生次數(shù),開發(fā)者應(yīng)該合并多次對(duì) DOM 的修改和對(duì)樣式的修改,然后一次性處理。
合并樣式操作比如:
var el = document.querySelector("div"); el.style.borderLeft = "1px"; el.style.borderRight = "2px"; el.style.padding = "5px";
可以合并成:
var el = document.querySelector("div"); el.style.cssText = "border-left: 1px; border-right: 1px; padding: 5px;"批量修改DOM
使元素脫離文檔流,再對(duì)其進(jìn)行操作,然后再把元素帶回文檔中,這種辦法可以有效減少重繪重排的次數(shù)。有三種基本辦法可以使元素脫離文檔流:
var ul = document.querySelector("ul"); ul.style.display = "none"; // code... 對(duì)ul進(jìn)行DOM操作 ul.style.display = "block";
var fragment = document.createDocumentFragment(); // code... 對(duì)fragment進(jìn)行DOM操作 var ul = document.querySelector("ul"); ul.appendChild(fragment)
var ul = document.querySelector("ul"); var cloneUl = ul.cloneNode(true); // code... 對(duì)clone節(jié)點(diǎn)進(jìn)行DOM操作 ul.parentNode.replaceChild(cloneUl, ul)緩存布局信息
前面已經(jīng)知道,獲取頁(yè)面布局信息,會(huì)導(dǎo)致瀏覽器強(qiáng)制刷新渲染隊(duì)列。所以減少這些操作是非常有必要的,開發(fā)者可以將第一次獲取到的頁(yè)面信息緩存到局部變量中,然后再操作局部變量,比如下面的偽代碼示例:
// 低效的 element.style.left = 1 + element.offsetLeft + "px"; element.style.top = 1 + element.offsetTop + "px"; if (element.offsetTop > 500) { stopAnimation(); } // 高效的 var offsetLeft = element.offsetLeft; var offsetTop = element.offsetTop; offsetLeft++; offsetTop++; element.style.left = offsetLeft + "px"; element.style.top = offsetTop + "px"; if (offsetTop > 500) { stopAnimation(); }總結(jié)
為了減少重繪重排帶來的性能消耗,可以通過以下幾點(diǎn)改善 web 應(yīng)用:
批量修改 DOM 和樣式
“離線”操作 DOM 樹,脫離文檔流
緩存到局部變量,減少頁(yè)面布局信息的訪問次數(shù)
參考
高性能JavaScript
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/100993.html
摘要:就如上面的概念一樣,單單改變?cè)氐耐庥^,肯定不會(huì)引起網(wǎng)頁(yè)重新生成布局,但當(dāng)瀏覽器完成重排之后,將會(huì)重新繪制受到此次重排影響的部分。因?yàn)殛?duì)列中,可能會(huì)有影響到這些值的操作,為了給我們最精確的值,瀏覽器會(huì)立即重排重繪。 showImg(http://ww1.sinaimg.cn/large/005Y4rCogy1fya3fh2jm3j30ku0dwtb2.jpg); 很多人都知道要減少瀏覽...
摘要:最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫即重力感應(yīng)視差效果功能。最終實(shí)現(xiàn)的效果會(huì)有卡頓現(xiàn)象。如果是后臺(tái)標(biāo)簽頁(yè)面,重繪頻率則會(huì)大大降低。較于,能得到更完整的加速的支持。 最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫(即重力感應(yīng)視差效果)功能。結(jié)果發(fā)現(xiàn)微信小程序有一些坑: 微信小程序不支持html5的DeviceOrie...
摘要:最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫即重力感應(yīng)視差效果功能。最終實(shí)現(xiàn)的效果會(huì)有卡頓現(xiàn)象。如果是后臺(tái)標(biāo)簽頁(yè)面,重繪頻率則會(huì)大大降低。較于,能得到更完整的加速的支持。 最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫(即重力感應(yīng)視差效果)功能。結(jié)果發(fā)現(xiàn)微信小程序有一些坑: 微信小程序不支持html5的DeviceOrie...
摘要:最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫即重力感應(yīng)視差效果功能。最終實(shí)現(xiàn)的效果會(huì)有卡頓現(xiàn)象。如果是后臺(tái)標(biāo)簽頁(yè)面,重繪頻率則會(huì)大大降低。較于,能得到更完整的加速的支持。 最近做微信小程序的開發(fā)時(shí),想做一個(gè)靠感知手機(jī)方向,使頁(yè)面上節(jié)點(diǎn)跟隨移動(dòng)的動(dòng)畫(即重力感應(yīng)視差效果)功能。結(jié)果發(fā)現(xiàn)微信小程序有一些坑: 微信小程序不支持html5的DeviceOrie...
閱讀 2169·2023-04-25 15:00
閱讀 2343·2021-11-18 13:14
閱讀 1154·2021-11-15 11:37
閱讀 3083·2021-09-24 13:55
閱讀 1221·2019-08-30 15:52
閱讀 2644·2019-08-29 12:35
閱讀 3359·2019-08-29 11:04
閱讀 1209·2019-08-26 12:13