摘要:就如上面的概念一樣,單單改變元素的外觀,肯定不會引起網頁重新生成布局,但當瀏覽器完成重排之后,將會重新繪制受到此次重排影響的部分。因為隊列中,可能會有影響到這些值的操作,為了給我們最精確的值,瀏覽器會立即重排重繪。
很多人都知道要減少瀏覽器的重排和重繪,但對其中的具體原理以及如何具體操作并不是很了解,當突然提起這個話題的時候,還是會一臉懵逼。希望大家可以耐著性子閱讀本文,仔細琢磨,徹底掌握這個知識點!
博客、前端積累文檔、公眾號、GitHub網頁生成過程:
HTML被HTML解析器解析成DOM 樹
css則被css解析器解析成CSSOM 樹
結合DOM樹和CSSOM樹,生成一棵渲染樹(Render Tree)
生成布局(flow),即將所有渲染樹的所有節點進行平面合成
將布局繪制(paint)在屏幕上
第四步和第五步是最耗時的部分,這兩步合起來,就是我們通常所說的渲染。
網上找了一張圖片,我加了注釋會更直觀一些:
渲染:網頁生成的時候,至少會渲染一次。
在用戶訪問的過程中,還會不斷重新渲染
重新渲染需要重復之前的第四步(重新生成布局)+第五步(重新繪制)或者只有第五個步(重新繪制)。
重排比重繪大:大,在這個語境里的意思是:誰能影響誰?
重繪:某些元素的外觀被改變,例如:元素的填充顏色
重排:重新生成布局,重新排列元素。
就如上面的概念一樣,單單改變元素的外觀,肯定不會引起網頁重新生成布局,但當瀏覽器完成重排之后,將會重新繪制受到此次重排影響的部分。
比如改變元素高度,這個元素乃至周邊dom都需要重新繪制。
也就是說:"重繪"不一定會出現"重排","重排"必然會出現"重繪"
重排(reflow): 概念:當DOM的變化影響了元素的幾何信息(DOM對象的位置和尺寸大小),瀏覽器需要重新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫做重排。
重排也叫回流,重排的過程以下面這種理解方式更清晰一些:
回流就好比向河里(文檔流)扔了一塊石頭(dom變化),激起漣漪,然后引起周邊水流受到波及,所以叫做回流常見引起重排屬性和方法
任何會改變元素幾何信息(元素的位置和尺寸大小)的操作,都會觸發重排,下面列一些栗子:
添加或者刪除可見的DOM元素;
元素尺寸改變——邊距、填充、邊框、寬度和高度
內容變化,比如用戶在input框中輸入文字
瀏覽器窗口尺寸改變——resize事件發生時
計算 offsetWidth 和 offsetHeight 屬性
設置 style 屬性的值
常見引起重排屬性和方法 | ||||
---|---|---|---|---|
width | height | margin | padding | |
display | border | position | overflow | |
clientWidth | clientHeight | clientTop | clientLeft | |
offsetWudth | offsetHeight | offsetTop | offsetLeft | |
scrollWidth | scrollHeight | scrollTop | scrollLeft | |
scrollIntoView() | scrollTo() | getComputedStyle() | ||
getBoundingClientRect() | scrollIntoViewIfNeeded() |
由于瀏覽器渲染界面是基于流失布局模型的,所以觸發重排時會對周圍DOM重新排列,影響的范圍有兩種:
全局范圍:從根節點html開始對整個渲染樹進行重新布局。
局部范圍:對渲染樹的某部分或某一個渲染對象進行重新布局
全局范圍重排:
hello
Name:BDing
male
- coding
- loving
當p節點上發生reflow時,hello和body也會重新渲染,甚至h5和ol都會收到影響。
局部范圍重排:
用局部布局來解釋這種現象:把一個dom的寬高之類的幾何信息定死,然后在dom內部觸發重排,就只會重新渲染該dom內部的元素,而不會影響到外界。
盡可能的減少重排的次數、重排范圍:重排需要更新渲染樹,性能花銷非常大:
它們的代價是高昂的,會破壞用戶體驗,并且讓UI展示非常遲緩,我們需要盡可能的減少觸發重排的次數。
重排的性能花銷跟渲染樹有多少節點需要重新構建有關系:
所以我們應該盡量以局部布局的形式組織html結構,盡可能小的影響重排的范圍。
而不是像全局范圍的示例代碼一樣一溜的堆砌標簽,隨便一個元素觸發重排都會導致全局范圍的重排。
重繪(Repaints):概念:
當一個元素的外觀發生改變,但沒有改變布局,重新把元素外觀繪制出來的過程,叫做重繪。
常見的引起重繪的屬性:
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樣式請求:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle(), 或者 IE的 currentStyle
我們在開發中,應該謹慎的使用這些style請求,注意上下文關系,避免一行代碼一個重排,這對性能是個巨大的消耗
重排優化建議就像上文提到的我們要盡可能的減少重排次數、重排范圍,這樣說很泛,下面是一些行之有效的建議,大家可以參考一下。
1. 分離讀寫操作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,因為渲染隊列本來就是空的,所以并沒有觸發重排,僅僅拿值而已。
2. 樣式集中改變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;";3. 緩存布局信息
// 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";4. 離線改變dom
隱藏要操作的dom
在要操作dom之前,通過display隱藏dom,當操作完成之后,才將元素的display屬性為可見,因為不可見的元素不會觸發重排和重繪。
dom.display = "none" // 修改dom樣式 dom.display = "block"
通過使用DocumentFragment創建一個dom碎片,在它上面批量操作dom,操作完成之后,再添加到文檔中,這樣只會觸發一次重排。
復制節點,在副本上工作,然后替換它!
5. position屬性為absolute或fixedposition屬性為absolute或fixed的元素,重排開銷比較小,不用考慮它對其他元素的影響
6. 優化動畫可以把動畫效果應用到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腳本執行效率低的關鍵因素之一,重排與重繪作為大廠經常出現的面試題,并且涉及的性能優化,這是前端必須掌握的基本概念/技能之一(敲黑板!)。
重排會不斷觸發這是不可避免的,但我們在開發時,應盡量按照文中的建議來組織代碼,這種優化,需要平時有意識的去做,一點一滴的去做,希望大家重視一下。
希望看完的朋友可以點個喜歡/關注,您的支持是對我最大的鼓勵。博客、前端積累文檔、公眾號、GitHub
以上2018.12.17
參考資料:
網頁性能管理詳解
優化CSS重排重繪與瀏覽器性能
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100349.html
摘要:并減少對樣式的請求。缺點暴露了模塊成員,外部可以修改模塊內部狀態。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。 HTML&&CSS基礎知識點整理 一、WEB標準:一系列標準的集合 1. 結構(Structure):html 語言:XHTML[可擴展超文本標識語言]和XML[可擴展標記語言] 2. 表現(Preasentation):css...
摘要:并減少對樣式的請求。缺點暴露了模塊成員,外部可以修改模塊內部狀態。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。 HTML&&CSS基礎知識點整理 一、WEB標準:一系列標準的集合 1. 結構(Structure):html 語言:XHTML[可擴展超文本標識語言]和XML[可擴展標記語言] 2. 表現(Preasentation):css...
摘要:重繪元素做了一些不影響排版的改變,比如背景色下劃線等等,只需要重新繪制的過程,叫做重繪。顯然回流帶來的代價大于重繪,因為重繪僅僅是重新畫一遍元素而已,但是重繪是重新計算重新畫。不然這會導致大量地讀寫這個結點的屬性。 瀏覽器的大概工作流程 以普通的HTML頁面為例: 解析HTML文檔,生成dom樹 解析css產生css規則樹 解析JavaScript,通過DOM-API來操作dom樹和...
摘要:重繪元素做了一些不影響排版的改變,比如背景色下劃線等等,只需要重新繪制的過程,叫做重繪。顯然回流帶來的代價大于重繪,因為重繪僅僅是重新畫一遍元素而已,但是重繪是重新計算重新畫。不然這會導致大量地讀寫這個結點的屬性。 瀏覽器的大概工作流程 以普通的HTML頁面為例: 解析HTML文檔,生成dom樹 解析css產生css規則樹 解析JavaScript,通過DOM-API來操作dom樹和...
摘要:瀏覽器會對發生變化的呈現器以及其子代標注為,表示需要進行標記分為兩種和。異步和同步異步,簡單來說,就是指瀏覽器為了盡可能減少和的操作,而將這些操作積攢起來,再統一做一次。 終于到了布局的部分了! 布局 當渲染對象被創建并添加到樹中,是沒有位置和大小的,計算這些值的過程稱為layout或reflow。布局是一個遞歸過程,由根渲染對象開始,對應html文檔元素,布局繼續遞歸的通過一些或所有...
閱讀 3403·2021-11-24 09:38
閱讀 3189·2021-11-22 09:34
閱讀 2098·2021-09-22 16:03
閱讀 2349·2019-08-29 18:37
閱讀 371·2019-08-29 16:15
閱讀 1761·2019-08-26 13:56
閱讀 853·2019-08-26 12:21
閱讀 2198·2019-08-26 12:15