不少人都知道要減小瀏覽器的重排和重繪,但對其中的具體原理以及如何具體操做並非很瞭解,當忽然提起這個話題的時候,仍是會一臉懵逼。但願你們能夠耐着性子閱讀本文,仔細琢磨,完全掌握這個知識點!css
博客、 前端積累文檔、 公衆號、 GitHub
第四步和第五步是最耗時的部分,這兩步合起來,就是咱們一般所說的渲染。html
網上找了一張圖片,我加了註釋會更直觀一些:前端
網頁生成的時候,至少會渲染一次。git
在用戶訪問的過程當中,還會不斷從新渲染github
從新渲染須要重複以前的第四步(從新生成佈局)+第五步(從新繪製)或者只有第五個步(從新繪製)。web
大,在這個語境裏的意思是:誰能影響誰?面試
就如上面的概念同樣,單單改變元素的外觀,確定不會引發網頁從新生成佈局,但當瀏覽器完成重排以後,將會從新繪製受到這次重排影響的部分。瀏覽器
好比改變元素高度,這個元素乃至周邊dom都須要從新繪製。
也就是說:"重繪"不必定會出現"重排","重排"必然會出現"重繪"緩存
當DOM的變化影響了元素的幾何信息(DOM對象的位置和尺寸大小),瀏覽器須要從新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫作重排。性能優化
重排也叫回流,重排的過程如下面這種理解方式更清晰一些:
迴流就比如向河裏(文檔流)扔了一塊石頭(dom變化),激起漣漪,而後引發周邊水流受到波及,因此叫作迴流
任何會改變元素幾何信息(元素的位置和尺寸大小)的操做,都會觸發重排,下面列一些栗子:
常見引發重排屬性和方法 | ||||
---|---|---|---|---|
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
開始對整個渲染樹進行從新佈局。全局範圍重排:
<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'
dom
碎片,在它上面批量操做dom,操做完成以後,再添加到文檔中,這樣只會觸發一次重排。position屬性爲absolute或fixed的元素,重排開銷比較小,不用考慮它對其餘元素的影響
動畫效果還應犧牲一些平滑,來換取速度,這中間的度本身衡量:
好比實現一個動畫,以1個像素爲單位移動這樣最平滑,可是reflow就會過於頻繁,大量消耗CPU資源,若是以3個像素爲單位移動則會好不少。
此部分來自優化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
參考資料: