不少人都知道要減小瀏覽器的重排和重繪,但對其中的具體原理以及如何具體操做並非很瞭解,當忽然提起這個話題的時候,仍是會一臉懵逼。但願你們能夠耐着性子閱讀本文,仔細琢磨,完全掌握這個知識點!css
第四步和第五步是最耗時的部分,這兩步合起來,就是咱們一般所說的渲染。前端
網上找了一張圖片,我加了註釋會更直觀一些:git
網頁生成的時候,至少會渲染一次。github
在用戶訪問的過程當中,還會不斷從新渲染web
從新渲染須要重複以前的第四步(從新生成佈局)+第五步(從新繪製)或者只有第五個步(從新繪製)。面試
大,在這個語境裏的意思是:誰能影響誰?瀏覽器
就如上面的概念同樣,單單改變元素的外觀,確定不會引發網頁從新生成佈局,但當瀏覽器完成重排以後,將會從新繪製受到這次重排影響的部分。緩存
好比改變元素高度,這個元素乃至周邊dom都須要從新繪製。
複製代碼
也就是說:"重繪"不必定會出現"重排","重排"必然會出現"重繪"性能優化
當DOM的變化影響了元素的幾何信息(DOM對象的位置和尺寸大小),瀏覽器須要從新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫作重排。
重排也叫回流,重排的過程如下面這種理解方式更清晰一些:
迴流就比如向河裏(文檔流)扔了一塊石頭(dom變化),激起漣漪,而後引發周邊水流受到波及,因此叫作迴流
任何會改變元素幾何信息(元素的位置和尺寸大小)的操做,都會觸發重排,下面列一些栗子:
常見引發重排屬性和方法 | |||
---|---|---|---|
width | height | margin | padding |
display | border | position | overflow |
clientWidth | clientHeight | clientTop | clientLeft |
offsetWidth | offsetHeight | offsetTop | offsetLeft |
scrollWidth | scrollHeight | scrollTop | scrollLeft |
scrollIntoView() | scrollTo() | getComputedStyle() | |
getBoundingClientRect() | scrollIntoViewIfNeeded() |
因爲瀏覽器渲染界面是基於流失佈局模型的,因此觸發重排時會對周圍DOM從新排列,影響的範圍有兩種:
html
開始對整個渲染樹進行從新佈局。全局範圍重排:
<body>
<div class="hello">
<h4>hello</h4>
<p><strong>Name:</strong>BDing</p>
<h5>male</h5>
<ol>
<li>coding</li>
<li>loving</li>
</ol>
</div>
</body>
複製代碼
當p節點上發生reflow時,hello和body也會從新渲染,甚至h5和ol都會收到影響。
局部範圍重排:
用局部佈局來解釋這種現象:把一個dom的寬高之類的幾何信息定死,而後在dom內部觸發重排,就只會從新渲染該dom內部的元素,而不會影響到外界。
重排鬚要更新渲染樹,性能花銷很是大:
它們的代價是高昂的,會破壞用戶體驗,而且讓UI展現很是遲緩,咱們須要儘量的減小觸發重排的次數。
重排的性能花銷跟渲染樹有多少節點須要從新構建有關係:
因此咱們應該儘可能以局部佈局的形式組織html
結構,儘量小的影響重排的範圍。
而不是像全局範圍的示例代碼同樣一溜的堆砌標籤,隨便一個元素觸發重排都會致使全局範圍的重排。
概念:
當一個元素的外觀發生改變,但沒有改變佈局,從新把元素外觀繪製出來的過程,叫作重繪。
常見的引發重繪的屬性:
color | border-style | visibility | background |
text-decoration | background-image | background-position | background-repeat |
outline-color | outline | outline-style | border-radius |
outline-width | box-shadow | background-size |
思考如下代碼將會觸發幾回渲染?
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
複製代碼
根據咱們上文的定義,這段代碼理論上會觸發4次重排+重繪,由於每一次都改變了元素的幾何屬性,實際上最後只觸發了一次重排,這都得益於瀏覽器的渲染隊列機制:
當咱們修改了元素的幾何屬性,致使瀏覽器觸發重排或重繪時。它會把該操做放進渲染隊列,等到隊列中的操做到了必定的數量或者到了必定的時間間隔時,瀏覽器就會批量執行這些操做。
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
複製代碼
這段代碼會觸發4次重排+重繪,由於在console
中你請求的這幾個樣式信息,不管什麼時候瀏覽器都會當即執行渲染隊列的任務,即便該值與你操做中修改的值沒關聯。
由於隊列中,可能會有影響到這些值的操做,爲了給咱們最精確的值,瀏覽器會當即重排+重繪。
強制刷新隊列的style樣式請求:
咱們在開發中,應該謹慎的使用這些style請求,注意上下文關係,避免一行代碼一個重排,這對性能是個巨大的消耗
就像上文提到的咱們要儘量的減小重排次數、重排範圍,這樣說很泛,下面是一些行之有效的建議,你們能夠參考一下。
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
複製代碼
仍是上面觸發4次重排+重繪的代碼,此次只觸發了一次重排:
在第一個console
的時候,瀏覽器把以前上面四個寫操做的渲染隊列都給清空了。剩下的console,由於渲染隊列原本就是空的,因此並無觸發重排,僅僅拿值而已。
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
複製代碼
雖然如今大部分瀏覽器有渲染隊列優化,不排除有些瀏覽器以及老版本的瀏覽器效率仍然低下:
建議經過改變class或者csstext屬性集中改變樣式
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
複製代碼
// bad 強制刷新 觸發兩次重排
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
// good 緩存佈局信息 至關於讀寫分離
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
複製代碼
隱藏要操做的dom
在要操做dom以前,經過display隱藏dom,當操做完成以後,纔將元素的display屬性爲可見,由於不可見的元素不會觸發重排和重繪。
dom.display = 'none'
// 修改dom樣式
dom.display = 'block'
複製代碼
經過使用DocumentFragment建立一個dom
碎片,在它上面批量操做dom,操做完成以後,再添加到文檔中,這樣只會觸發一次重排。
複製節點,在副本上工做,而後替換它!
position屬性爲absolute或fixed的元素,重排開銷比較小,不用考慮它對其餘元素的影響
能夠把動畫效果應用到position屬性爲absolute或fixed的元素上,這樣對其餘元素影響較小
動畫效果還應犧牲一些平滑,來換取速度,這中間的度本身衡量:
好比實現一個動畫,以1個像素爲單位移動這樣最平滑,可是reflow就會過於頻繁,大量消耗CPU資源,若是以3個像素爲單位移動則會好不少。
啓用GPPU加速
此部分來自優化CSS重排重繪與瀏覽器性能
GPU(圖像加速器):
GPU 硬件加速是指應用 GPU 的圖形性能對瀏覽器中的一些圖形操做交給 GPU 來完成,由於 GPU 是專門爲處理圖形而設計,因此它在速度和能耗上更有效率。
GPU 加速一般包括如下幾個部分:Canvas2D,佈局合成, CSS3轉換(transitions),CSS3 3D變換(transforms),WebGL和視頻(video)。
/* * 根據上面的結論 * 將 2d transform 換成 3d * 就能夠強制開啓 GPU 加速 * 提升動畫性能 */
div {
transform: translate3d(10px, 10px, 0);
}
複製代碼
重排也是致使DOM腳本執行效率低的關鍵因素之一,重排與重繪做爲大廠常常出現的面試題,而且涉及的性能優化,這是前端必須掌握的基本概念/技能之一(敲黑板!)。
重排會不斷觸發這是不可避免的,但咱們在開發時,應儘可能按照文中的建議來組織代碼,這種優化,須要平時有意識的去作,一點一滴的去作,但願你們重視一下。
以上2018.12.17
參考資料: