重排與重繪

重排與重繪

前言

此處需要首先說(shuō)明一下網(wǎng)頁(yè)從HTML文件變成屏幕上的畫(huà)面所經(jīng)歷的過(guò)程:

  1. HTML內(nèi)容被HTML解析器解析生成DOM樹(shù)
  2. CSS內(nèi)容被CSS解析器解析生產(chǎn)CSSOM樹(shù)
  3. DOM樹(shù)+CSSOM樹(shù)會(huì)生產(chǎn)Render Tree(渲染樹(shù))
  4. 生成布局艾帐,瀏覽器根據(jù)渲染樹(shù)來(lái)布局贸呢,以計(jì)算每個(gè)節(jié)點(diǎn)的幾何信息
  5. 將各個(gè)節(jié)點(diǎn)繪制到屏幕上

其中第4步為布局排列(flow),第5步為繪制(paint)弃衍,這兩部加起來(lái)也就是我們通常所說(shuō)的渲染

本次主題所說(shuō)的重排與重繪便是指網(wǎng)頁(yè)重新布局排列和重新繪制敬锐,英文概念分別為:Reflow , Repaint袜啃,也有些文章中稱(chēng)之為回流與重繪

目的

重繪與重排與我們的前端性能有什么關(guān)系呢盏袄?

在Web前端頁(yè)面的生命周期中芳绩,在網(wǎng)頁(yè)生成時(shí)蛾绎,瀏覽器會(huì)至少渲染一次頁(yè)面昆箕,并且,在用戶訪問(wèn)頁(yè)面過(guò)程中租冠,還可能會(huì)不斷觸發(fā)重排和重繪鹏倘,不管頁(yè)面發(fā)生了重繪還是重排,都會(huì)影響到網(wǎng)頁(yè)的性能顽爹,尤其是其中的重排纤泵,會(huì)使我們付出高額的性能代價(jià),因此我們應(yīng)盡量避免镜粤。

那么哪些操作會(huì)引發(fā)重排與重繪呢捏题?

  • 重繪:元素的外觀被改變,例如:元素的背景顏色發(fā)生變化

  • 重排:重新生成布局肉渴,重新排列元素公荧,例如:元素的尺寸、位置發(fā)生變化

重排(Reflow):

當(dāng)DOM的變化影響了元素的幾何信息(元素的的位置和尺寸大小)同规,瀏覽器需要重新計(jì)算元素的幾何屬性循狰,將其安放在界面中的正確位置窟社,這個(gè)過(guò)程叫做重排。

重排也叫回流绪钥,簡(jiǎn)單的說(shuō)就是重新生成布局灿里,重新排列元素。

以下情況會(huì)引發(fā)重排:

  • 頁(yè)面初始渲染(無(wú)法避免)
  • 添加或刪除可見(jiàn)的DOM元素
  • 元素位置的改變昧识,或者使用動(dòng)畫(huà)
  • 改變?cè)爻叽缒扑模热邕吘嗟涟恰⑻畛涔蚶恪⑦吙颉挾群透叨鹊?/li>
  • 填充內(nèi)容的改變侣灶,比如文本的改變或圖片大小改變而引起的計(jì)算值寬度和高度的改變
  • 瀏覽器窗口尺寸的變化(resize事件發(fā)生時(shí))
  • 設(shè)置 style 屬性的值甸祭,因?yàn)橥ㄟ^(guò)設(shè)置style屬性改變結(jié)點(diǎn)樣式的話,每一次設(shè)置都會(huì)觸發(fā)一次reflow
  • 讀取某些元素屬性:offsetLeft/Top/Height/Width, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, width/height, getComputedStyle(), currentStyle(IE)

重排影響的范圍:

由于瀏覽器渲染界面是基于流失布局模型的褥影,所以觸發(fā)重排時(shí)會(huì)對(duì)周?chē)鶧OM重新排列池户,影響的范圍有兩種:

  • 全局范圍:從根節(jié)點(diǎn)html開(kāi)始對(duì)整個(gè)渲染樹(shù)進(jìn)行重新布局
  • 局部范圍:對(duì)渲染樹(shù)的某部分或某一個(gè)渲染對(duì)象進(jìn)行重新布局

局部布局來(lái)解釋這種現(xiàn)象:把一個(gè)dom的寬高之類(lèi)的幾何信息定死,然后在dom內(nèi)部觸發(fā)重排凡怎,就只會(huì)重新渲染該dom內(nèi)部的元素校焦,而不會(huì)影響到外界。

重繪(Repaints):

當(dāng)一個(gè)元素的外觀發(fā)生改變统倒,但沒(méi)有改變布局, 瀏覽器重新把元素外觀繪制出來(lái)的過(guò)程寨典,叫做重繪。

重排必定會(huì)引發(fā)重繪房匆,但重繪不一定會(huì)引發(fā)重排耸成。

代價(jià)

重排的代價(jià)是高昂的,會(huì)破壞用戶體驗(yàn)浴鸿,并且讓UI展示非常遲緩井氢,重排重繪非常耗費(fèi)資源,是導(dǎo)致網(wǎng)頁(yè)性能低下的根本原因岳链。

DOM變動(dòng)和樣式變動(dòng)花竞,都會(huì)觸發(fā)重新渲染。但是掸哑,瀏覽器已經(jīng)很智能了约急,會(huì)盡量把所有的變動(dòng)集中在一起,排成一個(gè)隊(duì)列举户,然后一次性執(zhí)行烤宙,盡量避免多次重新渲染

// div元素有兩個(gè)樣式變動(dòng),但是瀏覽器只會(huì)觸發(fā)一次重排和重繪

div.style.color = 'blue';
div.style.marginTop = '30px';

如果寫(xiě)得不好俭嘁,就會(huì)觸發(fā)兩次重排和重繪

// 對(duì)div元素設(shè)置背景色以后躺枕,第二行要求瀏覽器給出該元素的位置,所以瀏覽器不得不立即重排

div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

優(yōu)化

我們應(yīng)盡量減少重排,最簡(jiǎn)單的方式是:1.減少重排次數(shù)拐云;2.縮小重排范圍

  1. 減少重排范圍

    • 應(yīng)該盡量以局部布局的形式組織HTML結(jié)構(gòu)罢猪,使各個(gè)結(jié)構(gòu)間相互獨(dú)立,當(dāng)某個(gè)結(jié)構(gòu)發(fā)生重排時(shí)叉瘩,不會(huì)影響到頁(yè)面上的其它結(jié)構(gòu)
    • 應(yīng)該盡可能在底層級(jí)的元素上設(shè)置樣式膳帕,削弱修改樣式時(shí),對(duì)頁(yè)面其它元素帶來(lái)影響
    • 不要使用table布局薇缅,可能很小的一個(gè)小改動(dòng)會(huì)造成整個(gè)table的重新布局
  2. 減少重排次數(shù)

    • 不要頻繁的操作樣式危彩,對(duì)于一個(gè)靜態(tài)頁(yè)面來(lái)說(shuō),明智且可維護(hù)的做法是更改類(lèi)名而不是修改樣式泳桦,對(duì)于動(dòng)態(tài)改變的樣式來(lái)說(shuō)汤徽,相較每次微小修改都直接觸及元素,更好的辦法是統(tǒng)一在 cssText 變量中編輯灸撰。

      // bad
      var left = 10;
      var top = 10;
      el.style.left = left + "px";
      el.style.top = top + "px";
      
      // good 
      el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
      
    • 離線操作元素:

      • 使用display:none

        一旦我們給元素設(shè)置 display:none 時(shí)(只有一次重排重繪)谒府,元素便不會(huì)再存在在渲染樹(shù)中,相當(dāng)于將其從頁(yè)面上“拿掉”浮毯,我們之后的操作將不會(huì)觸發(fā)重排和重繪完疫,添加足夠多的變更后,通過(guò) display屬性顯示(另一次重排重繪)债蓝。通過(guò)這種方式即使大量變更也只觸發(fā)兩次重排壳鹤。另外,visibility : hidden 的元素只對(duì)重繪有影響惦蚊,不影響重排

      • 通過(guò)documentFragment創(chuàng)建一個(gè) dom 碎片,在它上面批量操作 dom器虾,操作完成之后,再添加到文檔中蹦锋,這樣只會(huì)觸發(fā)一次重排

      • 復(fù)制節(jié)點(diǎn)兆沙,在副本上工作,然后替換它

        dom.display = 'none'
        // 修改dom樣式
        dom.display = 'block'
        
        
        var fragment = document.createDocumentFragment()
        // 操作dom
        document.body.appendChild(fragment);
        
        var newUL = oUl.cloneNode(true);
        // 操作dom
        parentElement.replaceChild(newUl, oUl);
        
  • 使用 absolute 或 fixed 脫離文檔流:使用 absolutefixed 脫離文檔流使用絕對(duì)定位會(huì)使的該元素單獨(dú)成為渲染樹(shù)中 body 的一個(gè)子元素莉掂,重排開(kāi)銷(xiāo)比較小葛圃,不會(huì)對(duì)其它節(jié)點(diǎn)造成太多影響

  • 分離讀寫(xiě)操作:DOM 的多個(gè)讀操作(或多個(gè)寫(xiě)操作),應(yīng)該放在一起憎妙。不要兩個(gè)讀操作之間库正,加入一個(gè)寫(xiě)操作

    // bad 強(qiáng)制刷新 觸發(fā)四次重排+重繪
    div.style.left = div.offsetLeft + 1 + 'px';
    div.style.top = div.offsetTop + 1 + 'px';
    div.style.right = div.offsetRight + 1 + 'px';
    div.style.bottom = div.offsetBottom + 1 + 'px';
    
    
    // good 緩存布局信息 相當(dāng)于讀寫(xiě)分離 觸發(fā)一次重排+重繪
    var curLeft = div.offsetLeft;
    var curTop = div.offsetTop;
    var curRight = div.offsetRight;
    var curBottom = div.offsetBottom;
    
    

觀察

我們可以通過(guò)Chrome觀察重排與重繪

打開(kāi)開(kāi)發(fā)者工具 => 點(diǎn)擊Performance => 點(diǎn)擊左側(cè)小圓點(diǎn) => 點(diǎn)擊刷新頁(yè)面 => 錄制=>查看數(shù)據(jù)

  • Loading: 網(wǎng)絡(luò)通信和HTML解析
  • Scripting: JavaScript執(zhí)行
  • Rendering: 樣式計(jì)算和布局,即重排
  • Painting: 重繪
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厘唾,一起剝皮案震驚了整個(gè)濱河市褥符,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抚垃,老刑警劉巖喷楣,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟大,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡铣焊,警方通過(guò)查閱死者的電腦和手機(jī)逊朽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)曲伊,“玉大人叽讳,你說(shuō)我怎么就攤上這事》啬迹” “怎么了岛蚤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)婿屹。 經(jīng)常有香客問(wèn)我灭美,道長(zhǎng)推溃,這世上最難降的妖魔是什么昂利? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮铁坎,結(jié)果婚禮上蜂奸,老公的妹妹穿的比我還像新娘。我一直安慰自己硬萍,他們只是感情好扩所,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著朴乖,像睡著了一般祖屏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上买羞,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天袁勺,我揣著相機(jī)與錄音,去河邊找鬼畜普。 笑死期丰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吃挑。 我是一名探鬼主播钝荡,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舶衬!你這毒婦竟也來(lái)了埠通?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逛犹,失蹤者是張志新(化名)和其女友劉穎端辱,沒(méi)想到半個(gè)月后蟹瘾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掠手,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年憾朴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喷鸽。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡众雷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出做祝,到底是詐尸還是另有隱情砾省,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布混槐,位于F島的核電站编兄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏声登。R本人自食惡果不足惜狠鸳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悯嗓。 院中可真熱鬧件舵,春花似錦、人聲如沸脯厨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)合武。三九已至临梗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稼跳,已是汗流浹背盟庞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岂贩,地道東北人茫经。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像萎津,于是被迫代替她去往敵國(guó)和親卸伞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容