前言
最近看了幕課網(wǎng) web 前端性能優(yōu)化的課程,其中說到了瀏覽器的回流(reflow) 及 重繪(repaint)葵擎。覺得以后面試或許會被問到所以做一下筆記:
課程從回流及重繪這兩個點延伸出了一個知識點就是 "css 會影響 javascrip 執(zhí)行時間導致 javascript 腳本變慢"谅阿。
瀏覽器渲染一個網(wǎng)頁的時候會啟用兩條線程:一條渲染javascript 腳本,另一條渲染 ui 即css 樣式的渲染酬滤。但這兩條線程是互斥的签餐。當javascript 線程運行的時候 ui 線程則會中止暫停,反之亦然盯串。
那這是為什么呢氯檐?
原因是,當ui 線程運行對頁面進行渲染的時候 js 腳本難免會涉及到頁面視圖上的一些樣式的改變体捏,為了使這個改變更加準確 js 腳本只好等待ui 線程渲染完成的時候才去執(zhí)行冠摄。
所以當一個頁面的元素樣式改動頻繁的時候ui 線程就會持續(xù)渲染,造成js 代碼反應慢半拍几缭,卡頓的情況河泳。
回流和重繪究竟是什么
回流:當render tree 的一部分或全部的元素因改變了自身的寬高,布局年栓,顯示或隱藏乔询,或者元素內(nèi)部的文字結(jié)構發(fā)生變化 導致需要重新構建頁面的時候,回流就產(chǎn)生了
重繪:當一個元素自身的寬高韵洋,布局竿刁,及顯示或隱藏沒有改變黄锤,而只是改變了元素的外觀風格的時候,就會產(chǎn)生重繪食拜。例如你改變了元素的background-color....
因此得出了一個結(jié)論:
回流必定觸發(fā)重繪鸵熟,而重繪不一定觸發(fā)回流
觸發(fā)回流的css 屬性有這些:
而觸發(fā)重繪的有這些:
現(xiàn)在我們知道了那些屬性會觸發(fā)回流,那如何避免回流呢负甸?
其實有兩個方法:
- 第一流强,不使用以上能觸發(fā)圖層回流的屬性,
- 第二呻待,建立一個圖層打月,讓回流在這些圖層里面進行,限制回流和重繪的范圍蚕捉,減少瀏覽器的運算工作量
先說說圖層吧
css 中有一個重要的概念的就是 "劃分圖層"奏篙。
在頁面加載完dom之后,瀏覽器會根據(jù)自身制定的一些特殊css 屬性對頁面進行劃分為一個個圖層迫淹。
例如:
- 一些有著 position: absolute 屬性的元素他們會被瀏覽器歸納到一個圖層中秘通,
- 另外一些 position: fixed 的元素也會被瀏覽器歸納到另外一個圖層中
- 還有一些例如:transform: translateZ(0).... 這些屬性都是視瀏覽器廠商制定的
圖層的優(yōu)點就是 :當一個元素在自己圖層內(nèi)發(fā)生變化的時候它的回流和重繪只會在圖層內(nèi)部發(fā)生,從而減少了瀏覽器對于重繪與回流的運算量敛熬。
那圖層內(nèi)部究竟干了什么呢肺稀?
首先,圖層對圖層里面的節(jié)點做回流和重布局運算应民,計算出他們的樣式,然后话原,對它們生成圖形和位置。緊跟著诲锹,將每個節(jié)點繪制填充到圖層位圖中繁仁。再然后,圖層作為紋理發(fā)送到GPU 上辕狰。最后改备,將所有圖層合并,顯示在瀏覽器的頁面上蔓倍。
圖層雖好悬钳,但是如果濫用圖層的話就會讓頁面因為進行了太多合并運算導致頁面卡頓。
所以說偶翅,圖層的用途應該讓給那些經(jīng)常要進行位置結(jié)構默勾,布局變換的元素去使用。例如輪播圖聚谁。
那我們?nèi)鐒?chuàng)建一個圖層呢母剥?
就拿chrome 來說,創(chuàng)建一個圖層要有以下其中一個條件:
- 一個dom 元素擁有 3d 或 透視變換的css 屬性(persepective, transform)
- video 標簽
- 擁有3d 上下文或加速 2d 上下文的 canvas 節(jié)點
- 混合插件 flash
- 自己做的opacity 動畫 或 使用一個動畫 webkit 變換的元素
- 擁有 translate3d 或 translateZ 這兩會使 GPU 加速的屬性
- 一個包含復合層子節(jié)點的元素。(有點繞环疼,可以這樣想:其實本身整個網(wǎng)頁頁面就是一個圖層习霹,html 標簽下包含著若干的標簽 head, body,..... 這樣便滿足了這個條件了)
- 元素有一個其 z-index 比它低的兄弟節(jié)點。由于 z-index 控制的是元素上下層的關系炫隶,所以當上下層關系變換的時候就需要一個圖層去渲染淋叶,因此滿足這個條件的元素也會被創(chuàng)建一個圖層
那如果我們使用屬性又有那些可替換的屬性可以優(yōu)化回流與重繪
一共有一下9點:
- 用transform 代替 top,left 伪阶,margin-top煞檩, margin-left... 這些位移屬性
- 用opacity 代替 visibility,但是要同時有translate3d 或 translateZ 這些可以創(chuàng)建的圖層的屬性存在才可以阻止回流
- 不要使用 js 代碼對dom 元素設置多條樣式栅贴,選擇用一個 className 代替之斟湃。
- 如果確實需要用 js 對 dom 設置多條樣式那么可以將這個dom 先隱藏,然后再對其設置
- 不要在循環(huán)內(nèi)獲取dom 的樣式例如:offsetWidth, offsetHeight, clientWidth, clientHeight... 這些檐薯。瀏覽器有一個回流的緩沖機制凝赛,即多個回流會保存在一個棧里面,當這個棧滿了瀏覽器便會一次性觸發(fā)所有樣式的更改且刷新這個棧厨剪。但是如果你多次獲取這些元素的實際樣式哄酝,瀏覽器為了給你一個準確的答案便會不停刷新這個緩沖棧友存,導致頁面回流增加祷膳。
所以為了避免這個問題,應該用一個變量保存在循環(huán)體外屡立。 - 不要使用table 布局直晨,因為table 的每一個行甚至每一個單元格的樣式更新都會導致整個table 重新布局单默,這也就是為什么都使用div+css進行布局了
- 動畫的速度按照業(yè)務按需決定
- 對于頻繁變化的元素應該為其加一個 transform 屬性跟束,對于視頻使用video 標簽
- 必要時可以開啟 GPU 加速,但是不能濫用