深入理解瀏覽器渲染原理:Repaint, Reflow

引用自 Rendering: repaint, reflow/relayout, restyle

瀏覽器在下載好HTML植影、CSS、JS等文件后秋泄,是如何將這些內(nèi)容組裝成絢麗的頁面呈現(xiàn)給用戶呢?這兒我們可以深入了解一下這個過程:

渲染過程

不同的瀏覽器渲染過程實際上并不相同,但是依舊存在相一致的部分小作,大致過程如下圖:


render.png
  • 瀏覽器解析HTML的源碼,然后構(gòu)造出一個DOM(學(xué)名:文檔對象模型)樹稼钩,這棵樹包含了所有的DOM結(jié)點與其中囊括的文本內(nèi)容顾稀,在JS中,我們可以通過document中的一些方法(例如:getElementById, getElementsByTagName, querySelector...)拿到HTML結(jié)點所對應(yīng)的DOM坝撑,并對其進(jìn)行一些操作静秆。一般來說,DOM樹是以<html>作為根結(jié)點巡李。
  • 接下來抚笔,瀏覽器開始對CSS文件內(nèi)容進(jìn)行解析。一般來說击儡,瀏覽器會先查找內(nèi)聯(lián)樣式塔沃,然后應(yīng)用CSS文件中定義的樣式,最后再是應(yīng)用瀏覽器默認(rèn)樣式。
  • 然后蛀柴,就是構(gòu)造渲染樹的過程螃概。渲染樹與DOM樹有些類似,但并不完全相同鸽疾。例如我們定義了一個<div style="display:none;"></div>的DOM吊洼,實際上這個結(jié)點并不會在渲染樹里存在,類似的還有其他的不可見元素制肮。另一方面冒窍,DOM中同一類型的結(jié)點可能會存在多個,每個結(jié)點實際上都是一個盒子豺鼻,包括寬度综液、高度、邊框大小儒飒、邊距等等谬莹。
  • 一旦渲染樹構(gòu)造好了,接下來瀏覽器會將其繪制出來桩了。

森林和樹

我們先看一段HTML代碼:

<html>
<head>
  <title>Beautiful page</title>
</head>
<body>
    
  <p>
    Once upon a time there was 
    a looong paragraph...
  </p>
  
  <div style="display: none">
    Secret message
  </div>
  
  <div>![](...)</div>
  ...
 
</body>
</html>

其DOM樹大致如此:

documentElement (html)
    head
        title
    body
        p
            [text node]
        
        div 
            [text node]
        
        div
            img
        
        ...

渲染樹為DOM樹中可視的部分:

root (RenderView)
    body
        p
            line 1
            line 2
            line 3
            ...
        
        div
            img
        
    ...

渲染樹的根結(jié)點囊括了所有的可視元素附帽,它是瀏覽器窗口的一部分,并且能夠進(jìn)行伸縮調(diào)整井誉。一般來說蕉扮,渲染區(qū)域為自瀏覽器左上角(0,0)起始,終止于右下角(window.innerWidth, window.innerHeight)的矩形部分颗圣。

重繪與回流

當(dāng)?shù)谝淮未蜷_一個頁面時喳钟,至少會有一次重繪和回流。之后欠啤,如果渲染樹發(fā)生了變動荚藻,那么可能會觸發(fā)重繪或回流中的一個或二者。

  1. 如果渲染樹的結(jié)點發(fā)生了結(jié)構(gòu)性變化洁段,例如寬度应狱、高度或者位置上的變化時,那么會觸發(fā)Reflow(回流)的邏輯祠丝。我們第一次進(jìn)入一個頁面時便會至少觸發(fā)一次這個邏輯疾呻。
  2. 如果渲染樹結(jié)點發(fā)生了非結(jié)構(gòu)性變化,例如背景色等的變化時写半,那么會觸發(fā)Repaint(重繪)的邏輯岸蜗。

重繪與回流都會導(dǎo)致體驗上的不佳。

觸發(fā)Repaint或Reflow

我們具體看看哪些操作會導(dǎo)致重繪或回流:

  • 增加叠蝇、刪除璃岳、修改DOM結(jié)點
  • 使用display:none;的方式隱藏一個結(jié)點會導(dǎo)致repaint與reflow,使用visibility:hidden;進(jìn)行dom隱藏僅僅導(dǎo)致repaint(沒有結(jié)構(gòu)性變化,僅僅看不見而已)
  • 移動dom或著該dom進(jìn)行動畫
  • 添加新的樣式铃慷,或者修改某個樣式
  • 用戶的一些操作諸如改變?yōu)g覽器窗口大小单芜,調(diào)整字體大小,滾動等等

我們看些例子:

var bstyle = document.body.style; // cache
 
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; // another reflow and a repaint
 
bstyle.color = "blue"; // repaint only, no dimensions changed
bstyle.backgroundColor = "#fad"; // repaint
 
bstyle.fontSize = "2em"; // reflow, repaint
 
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

有些reflow的操作代價會比一般的高出許多犁柜≈摒可以試著想象出一棵渲染樹,如果只是自上而下的對某些dom進(jìn)行一些寬高等方面的調(diào)整馋缅,那么并不會導(dǎo)致整棵渲染樹太大的變動扒腕,而如果是頻繁調(diào)整部分結(jié)點在整個渲染樹的位置,這種調(diào)整往往是“牽一而動全身”萤悴,代價就會非常高了瘾腰。

瀏覽器的處理方式

既然渲染樹的reflow或repaint的代價十分高昂,那么不得不采取一些優(yōu)化的方式覆履,瀏覽器對此有一些針對性的舉措居灯。一種策略便是延遲。瀏覽器會將一些變動放在一個隊列中内狗,當(dāng)達(dá)到一定規(guī)模或者延遲的時間已到义锥,那么會一次將這些變動反應(yīng)到渲染樹中柳沙。但是這種策略會有一定的弊端,當(dāng)我們執(zhí)行一些腳本時可能會導(dǎo)致瀏覽器不得不提前讓repaint或reflow進(jìn)行完畢拌倍,例如我們需要獲取一些樣式信息時赂鲤,諸如:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. getComputedStyle(), or currentStyle in IE
      為了讓JS獲取到最終的樣式,瀏覽器不得不將緩沖隊列里reflow或repaint過程執(zhí)行完畢柱恤。所以数初,我們一般需要避免一連串的設(shè)置或獲取dom樣式:
// no-no!
el.style.left = el.offsetLeft + 10 + "px";

壓縮repaints或reflows

我們有一些策略去盡量消除或減小reflow/repaint所帶來的負(fù)面影響。

  • 不要一個一個地去改變結(jié)點的樣式梗顺,而可以通過設(shè)置cssText一次性將結(jié)點樣式修改完畢:
// bad
var left = 10,
    top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
 
// better 
el.className += " theclassname";
 
// or when top and left are calculated dynamically...
 
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
  • 對多個dom進(jìn)行操作時泡孩,我們可以使用一種“離線”方式∷掳“離線”意味著我們我們先在渲染樹之外進(jìn)行操作仑鸥。

    • 創(chuàng)建一個documentFragment去保持住我們要操作的dom
    • 克隆你需要進(jìn)行操作的結(jié)點,進(jìn)行操作后再將其與原始結(jié)點作交換
  • 不要經(jīng)常去訪問計算后的樣式变屁,如果可以眼俊,可以先將這些信息緩存下來。

// no-no!
for(big; loop; here) {
    el.style.left = el.offsetLeft + 10 + "px";
    el.style.top  = el.offsetTop  + 10 + "px";
}
 
// better
var left = el.offsetLeft,
    top  = el.offsetTop
    esty = el.style;
for(big; loop; here) {
    left += 10;
    top  += 10;
    esty.left = left + "px";
    esty.top  = top  + "px";
}
  • 一般說來粟关,如果對一個使用了絕對布局的dom進(jìn)行操作疮胖,并不會導(dǎo)致大量的reflow,不過絕對布局喪失了許多靈活性,很少有人會使用絕對定位的方式進(jìn)行主界面的布局澎灸。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末院塞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子击孩,更是在濱河造成了極大的恐慌迫悠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巩梢,死亡現(xiàn)場離奇詭異创泄,居然都是意外死亡,警方通過查閱死者的電腦和手機括蝠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門鞠抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忌警,你說我怎么就攤上這事搁拙。” “怎么了法绵?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵箕速,是天一觀的道長。 經(jīng)常有香客問我朋譬,道長盐茎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任徙赢,我火速辦了婚禮字柠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狡赐。我一直安慰自己窑业,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布枕屉。 她就那樣靜靜地躺著常柄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搀擂。 梳的紋絲不亂的頭發(fā)上拐纱,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音哥倔,去河邊找鬼秸架。 笑死,一個胖子當(dāng)著我的面吹牛咆蒿,可吹牛的內(nèi)容都是我干的东抹。 我是一名探鬼主播蚂子,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缭黔!你這毒婦竟也來了食茎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤馏谨,失蹤者是張志新(化名)和其女友劉穎别渔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惧互,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡哎媚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喊儡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拨与。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖艾猜,靈堂內(nèi)的尸體忽然破棺而出买喧,到底是詐尸還是另有隱情,我是刑警寧澤匆赃,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布淤毛,位于F島的核電站,受9級特大地震影響算柳,放射性物質(zhì)發(fā)生泄漏钱床。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一埠居、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧事期,春花似錦滥壕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至唠倦,卻和暖如春称鳞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稠鼻。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工冈止, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人候齿。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓熙暴,卻偏偏與公主長得像闺属,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子周霉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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