JS基礎知識儲備(回流與重繪及優(yōu)化策略)

1紊浩、瀏覽器渲染過程

image

從上面這個圖上旭等,我們可以看到酌呆,瀏覽器渲染過程如下:

  • 解析HTML,生成DOM樹搔耕,解析CSS隙袁,生成CSSOM樹
  • 將DOM樹和CSSOM樹結合,生成渲染樹(Render Tree)
  • Layout(回流):根據(jù)生成的渲染樹,進行回流(Layout)菩收,得到節(jié)點的幾何信息(位置梨睁,大小)
  • Painting(重繪):根據(jù)渲染樹以及回流得到的幾何信息娜饵,得到節(jié)點的絕對像素
  • Display:將像素發(fā)送給GPU坡贺,展示在頁面上。(這一步其實還有很多內(nèi)容箱舞,比如會在GPU將多個合成層合并為同一個層遍坟,并展示在頁面中。而css3硬件加速的原理則是新建合成層褐缠,這里我們不展開政鼠,之后有機會會寫一篇博客)

渲染過程看起來很簡單,讓我們來具體了解下每一步具體做了什么队魏。

生成渲染樹

image

為了構建渲染樹,瀏覽器主要完成了以下工作:

  1. 從DOM樹的根節(jié)點開始遍歷每個可見節(jié)點万搔。
  2. 對于每個可見的節(jié)點胡桨,找到CSSOM樹中對應的規(guī)則,并應用它們瞬雹。
  3. 根據(jù)每個可見節(jié)點以及其對應的樣式昧谊,組合生成渲染樹。

第一步中酗捌,既然說到了要遍歷可見的節(jié)點呢诬,那么我們得先知道,什么節(jié)點是不可見的胖缤。不可見的節(jié)點包括:

  • 一些不會渲染輸出的節(jié)點尚镰,比如script、meta哪廓、link等狗唉。
  • 一些通過css進行隱藏的節(jié)點。比如display:none涡真。注意分俯,利用visibility和opacity隱藏的節(jié)點,還是會顯示在渲染樹上的哆料。只有display:none的節(jié)點才不會顯示在渲染樹上缸剪。

注意:渲染樹只包含可見的節(jié)點

2、回流與重繪

1东亦、概述

  • 回流:當render tree中的一部分(或全部)杏节,因為元素的規(guī)模尺寸、布局、隱藏等改變而需要重新構建拢锹,這就是回流(reflow)
  • 重繪:回流完成后谣妻,瀏覽器會重新繪制受影響的部分,這就是重繪過程卒稳。
    當render tree中的一些元素需要更新屬性蹋半,而這些屬性只是影響元素的外觀、風格充坑,而不影響布局减江,則稱為重繪(repaints)

2、何時發(fā)生回流重繪

當頁面布局和幾何屬性改變時就需要回流

  • 添加或刪除可見的DOM元素
  • 元素的位置發(fā)生變化(offsetWidth捻爷、offsetHeight)
  • 元素尺寸發(fā)生變化(包括外邊距辈灼、內(nèi)邊框、邊框大小也榄、高度和寬度等)
  • 內(nèi)容發(fā)生變化(文本巡莹、圖片、input框)
  • 頁面渲染初始化
  • 瀏覽器窗口尺寸改變(回流是根據(jù)視口的大小來計算元素的位置和大小的)
  • 增加或移除樣式表
  • 操作class屬性
  • 改變字體
  • 激活偽類(如:hover)

注意:回流一定會觸發(fā)重繪甜紫,而重繪不一定會回流
(比如在body最前面插入一個元素降宅,會導致整個render tree回流,如果在body后面插入一個元素囚霸,則不會影響前面元素的回流)

3腰根、瀏覽器的優(yōu)化機制

現(xiàn)代的瀏覽器都是很聰明的,由于每次重排都會造成額外的計算消耗拓型,因此大多數(shù)瀏覽器都會通過隊列化修改并批量執(zhí)行來優(yōu)化重排過程额嘿。瀏覽器會將修改操作放入到隊列里,直到過了一段時間或者操作達到了一個閾值劣挫,才清空隊列册养。但是!當你獲取布局信息的操作的時候揣云,會強制隊列刷新捕儒,比如當你訪問以下屬性或者使用以下方法:

  • offsetTop、offsetLeft邓夕、offsetWidth刘莹、offsetHeight
  • scrollTop、scrollLeft焚刚、scrollWidth点弯、scrollHeight
  • clientTop、clientLeft矿咕、clientWidth抢肛、clientHeight
  • getComputedStyle() 或者 IE中:currentStyle
  • getBoundingClientRect
  • 具體可以訪問這個網(wǎng)站:https://gist.github.com/pauli...

以上屬性和方法都需要返回最新的布局信息狼钮,因此瀏覽器不得不清空隊列,觸發(fā)回流重繪來返回正確的值捡絮。因此熬芜,我們在修改樣式的時候,最好避免使用上面列出的屬性福稳,他們都會刷新渲染隊列涎拉。如果要使用它們,最好將值緩存起來的圆。

3鼓拧、如何減少回流重繪

1、最小化重繪

減少對render tree的操作越妈,并減少一些對style信息的請求季俩,合理利用瀏覽器的優(yōu)化策略

const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
  • 使用cssText
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
  • 修改CSS的classname
const el = document.getElementById('test');
el.className += ' active';

2、批量處理DOM

當我們需要對DOM對一系列修改的時候梅掠,可以通過以下步驟減少回流重繪次數(shù):

  1. 使元素脫離文檔流
  2. 對其進行多次修改
  3. 將元素帶回到文檔中酌住。

該過程的第一步和第三步可能會引起回流,但是經(jīng)過第一步之后阎抒,對DOM的所有修改都不會引起回流赂韵,因為它已經(jīng)不在渲染樹了。

有三種方式可以讓DOM脫離文檔流:

  • 隱藏元素挠蛉,應用修改,重新顯示
  • 使用文檔片段(document fragment)在當前DOM之外構建一個子樹肄满,再把它拷貝回文檔谴古。
  • 將原始元素拷貝到一個脫離文檔的節(jié)點中,修改節(jié)點后稠歉,再替換原始的元素掰担。

3、避免觸發(fā)同步布局事件

當我們訪問元素的一些屬性的時候怒炸,會導致瀏覽器強制清空隊列带饱,進行強制同步布局。舉個例子阅羹,比如說我們想將一個p標簽數(shù)組的寬度賦值為一個元素的寬度勺疼,我們可能寫出這樣的代碼:

function initP() {
    for (let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = box.offsetWidth + 'px';
    }
}

這段代碼看上去是沒有什么問題,可是其實會造成很大的性能問題捏鱼。在每次循環(huán)的時候执庐,都讀取了box的一個offsetWidth屬性值,然后利用它來更新p標簽的width屬性导梆。這就導致了每一次循環(huán)的時候轨淌,瀏覽器都必須先使上一次循環(huán)中的樣式更新操作生效迂烁,才能響應本次循環(huán)的樣式讀取操作。每一次循環(huán)都會強制瀏覽器刷新隊列递鹉。我們可以優(yōu)化為:

const width = box.offsetWidth;
function initP() {
    for (let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = width + 'px';
    }
}

4盟步、對于復雜動畫效果,使用絕對定位讓其脫離文檔流

對于復雜動畫效果,由于會經(jīng)常的引起回流重繪躏结,因此却盘,我們可以使用絕對定位,讓它脫離文檔流窜觉。否則會引起父元素以及后續(xù)元素頻繁的回流谷炸。例子

5、css3硬件加速(GPU加速)

使用css3硬件加速禀挫,可以讓transform旬陡、opacity、filters這些動畫不會引起回流重繪 语婴。但是對于動畫的其它屬性描孟,比如background-color這些,還是會引起回流重繪的砰左,不過它還是可以提升這些動畫的性能匿醒。

如何使用

  • transform
  • opacity
  • filters
  • Will-change

效果

我們可以先看個例子。我通過使用chrome的Performance捕獲了一段時間的回流重繪情況缠导,實際結果如下圖:

image

從圖中我們可以看出廉羔,在動畫進行的時候,沒有發(fā)生任何的回流重繪僻造。

重點

  • 使用css3硬件加速憋他,可以讓transform、opacity髓削、filters這些動畫不會引起回流重繪
  • 對于動畫的其它屬性竹挡,比如background-color這些,還是會引起回流重繪的立膛,不過它還是可以提升這些動畫的性能揪罕。

css3硬件加速的坑

  • 如果你為太多元素使用css3硬件加速,會導致內(nèi)存占用較大宝泵,會有性能問題好啰。
  • 在GPU渲染字體會導致抗鋸齒無效。這是因為GPU和CPU的算法不同鲁猩。因此如果你不在動畫結束的時候關閉硬件加速坎怪,會產(chǎn)生字體模糊。
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廓握,一起剝皮案震驚了整個濱河市搅窿,隨后出現(xiàn)的幾起案子嘁酿,更是在濱河造成了極大的恐慌,老刑警劉巖男应,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闹司,死亡現(xiàn)場離奇詭異,居然都是意外死亡沐飘,警方通過查閱死者的電腦和手機游桩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耐朴,“玉大人借卧,你說我怎么就攤上這事∩盖停” “怎么了铐刘?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長影晓。 經(jīng)常有香客問我镰吵,道長,這世上最難降的妖魔是什么挂签? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任疤祭,我火速辦了婚禮,結果婚禮上饵婆,老公的妹妹穿的比我還像新娘勺馆。我一直安慰自己,他們只是感情好侨核,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布谓传。 她就那樣靜靜地躺著,像睡著了一般芹关。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上紧卒,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天侥衬,我揣著相機與錄音,去河邊找鬼跑芳。 笑死轴总,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的博个。 我是一名探鬼主播怀樟,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼盆佣!你這毒婦竟也來了往堡?” 一聲冷哼從身側響起械荷,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虑灰,沒想到半個月后吨瞎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡穆咐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年颤诀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片对湃。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡崖叫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拍柒,到底是詐尸還是另有隱情心傀,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布斤儿,位于F島的核電站剧包,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏往果。R本人自食惡果不足惜疆液,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陕贮。 院中可真熱鬧堕油,春花似錦、人聲如沸肮之。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戈擒。三九已至眶明,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐高,已是汗流浹背搜囱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柑土,地道東北人蜀肘。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像稽屏,于是被迫代替她去往敵國和親扮宠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345