DOM 操作成本到底高在哪兒击蹲?引用

引用文章署拟,如有冒犯,私我立即刪除


DOM 操作成本到底高在哪兒歌豺?

從我接觸前端到現(xiàn)在推穷,一直聽到的一句話:操作DOM的成本很高,不要輕易去操作DOM类咧。尤其是React缨恒、vue等MV*框架的出現(xiàn),數(shù)據(jù)驅動視圖的模式越發(fā)深入人心轮听,jQuery時代提供的強大便利地操作DOM的API在前端工程里用的越來越少骗露。刨根問底,這里說的成本血巍,到底高在哪兒呢萧锉?

什么是DOM

Document Object Model 文檔對象模型

什么是DOM?可能很多人第一反應就是div述寡、p柿隙、span等html標簽(至少我是)叶洞,但要知道,DOM是Model禀崖,是Object Model衩辟,對象模型,是為HTML(and XML)提供的API波附。HTML(Hyper Text Markup Language)是一種標記語言艺晴,HTML在DOM的模型標準中被視為對象,DOM只提供編程接口掸屡,卻無法實際操作HTML里面的內容封寞。但在瀏覽器端,前端們可以用腳本語言(JavaScript)通過DOM去操作HTML內容仅财。

那么問題來了狈究,只有JavaScript才能調用DOM這個API嗎?

答案是NO盏求。

Python也可以訪問DOM抖锥。所以DOM不是提供給Javascript的API,也不是Javascript里的API碎罚。

PS: 實質上還存在CSSOM:CSS Object Model磅废,瀏覽器將CSS代碼解析成樹形的數(shù)據(jù)結構,與DOM是兩個獨立的數(shù)據(jù)結構魂莫。

瀏覽器渲染過程

討論DOM操作成本,肯定要先了解該成本的來源爹耗,那么就離不開瀏覽器渲染耙考。

這里暫只討論瀏覽器拿到HTML之后開始解析、渲染潭兽。(怎么拿到HTML資源的可能后續(xù)另開篇總結吧倦始,什么握握握手啊揮揮揮揮手啊,萬惡的flag...)

解析HTML山卦,構建DOM樹(這里遇到外鏈鞋邑,此時會發(fā)起請求)

解析CSS,生成CSS規(guī)則樹

合并DOM樹和CSS規(guī)則账蓉,生成render樹

布局render樹(Layout/reflow)枚碗,負責各元素尺寸、位置的計算

繪制render樹(paint)铸本,繪制頁面像素信息

瀏覽器會將各層的信息發(fā)送給GPU肮雨,GPU將各層合成(composite),顯示在屏幕上

1.構建DOM樹

<html>

?<head>

? ?<meta name="viewport" content="width=device-width,initial-scale=1">

? ?<link href="style.css" rel="stylesheet">

? ?<title>Critical Path</title>

?</head>

?<body>

? ?<p>Hello <span>web performance</span> students!</p>

? ?<div><img src="awesome-photo.jpg"></div>

?</body>

</html>

無論是DOM還是CSSOM箱玷,都是要經過 Bytes→characters→tokens→nodes→objectmodel這個過程怨规。

DOM樹構建過程:當前節(jié)點的所有子節(jié)點都構建好后才會去構建當前節(jié)點的下一個兄弟節(jié)點陌宿。

2.構建CSSOM樹

上述也提到了CSSOM的構建過程,也是樹的結構波丰,在最終計算各個節(jié)點的樣式時壳坪,瀏覽器都會先從該節(jié)點的普遍屬性(比如body里設置的全局樣式)開始,再去應用該節(jié)點的具體屬性掰烟。還有要注意的是爽蝴,每個瀏覽器都有自己默認的樣式表,因此很多時候這棵CSSOM樹只是對這張默認樣式表的部分替換媚赖。

3.生成render樹

DOM樹和CSSOM樹合并生成render樹

簡單描述這個過程:

DOM樹從根節(jié)點開始遍歷可見節(jié)點霜瘪,這里之所以強調了“可見”,是因為如果遇到設置了類似 display:none;的不可見節(jié)點惧磺,在render過程中是會被跳過的(但 visibility:hidden;opacity:0這種仍舊占據(jù)空間的節(jié)點不會被跳過render)颖对,保存各個節(jié)點的樣式信息及其余節(jié)點的從屬關系。

4.Layout 布局

有了各個節(jié)點的樣式信息和屬性磨隘,但不知道各個節(jié)點的確切位置和大小缤底,所以要通過布局將樣式信息和屬性轉換為實際可視窗口的相對大小和位置。

5.Paint 繪制

萬事俱備番捂,最后只要將確定好位置大小的各節(jié)點个唧,通過GPU渲染到屏幕的實際像素。

Tips

在上述渲染過程中设预,前3點可能要多次執(zhí)行徙歼,比如js腳本去操作dom、更改css樣式時鳖枕,瀏覽器又要重新構建DOM魄梯、CSSOM樹,重新render宾符,重新layout酿秸、paint;

Layout在Paint之前魏烫,因此每次Layout重新布局(reflow 回流)后都要重新出發(fā)Paint渲染辣苏,這時又要去消耗GPU;

Paint不一定會觸發(fā)Layout哄褒,比如改個顏色改個背景稀蟋;(repaint 重繪)

圖片下載完也會重新出發(fā)Layout和Paint;

何時觸發(fā)reflow和repaint

reflow(回流): 根據(jù)Render Tree布局(幾何屬性)呐赡,意味著元素的內容糊治、結構、位置或尺寸發(fā)生了變化罚舱,需要重新計算樣式和渲染樹井辜;

repaint(重繪): 意味著元素發(fā)生的改變只影響了節(jié)點的一些樣式(背景色绎谦,邊框顏色,文字顏色等)粥脚,只需要應用新樣式繪制這個元素就可以了窃肠;

reflow回流的成本開銷要高于repaint重繪,一個節(jié)點的回流往往回導致子節(jié)點以及同級節(jié)點的回流刷允;

GoogleChromeLabs 里面有一個csstriggers冤留,列出了各個CSS屬性對瀏覽器執(zhí)行Layout、Paint树灶、Composite的影響纤怒。

引起reflow回流

現(xiàn)代瀏覽器會對回流做優(yōu)化,它會等到足夠數(shù)量的變化發(fā)生天通,再做一次批處理回流泊窘。

頁面第一次渲染(初始化)

DOM樹變化(如:增刪節(jié)點)

Render樹變化(如:padding改變)

瀏覽器窗口resize

獲取元素的某些屬性:

瀏覽器為了獲得正確的值也會提前觸發(fā)回流,這樣就使得瀏覽器的優(yōu)化失效了像寒,這些屬性包括offsetLeft、offsetTop诺祸、offsetWidth携悯、offsetHeight、 scrollTop/Left/Width/Height筷笨、clientTop/Left/Width/Height憔鬼、調用了getComputedStyle()或者IE的currentStyle

引起repaint重繪

reflow回流必定引起repaint重繪,重繪可以單獨觸發(fā)

背景色胃夏、顏色轴或、字體改變(注意:字體大小發(fā)生變化時,會觸發(fā)回流)

優(yōu)化reflow构订、repaint觸發(fā)次數(shù)

避免逐個修改節(jié)點樣式侮叮,盡量一次性修改

使用DocumentFragment將需要多次修改的DOM元素緩存避矢,最后一次性append到真實DOM中渲染

可以將需要多次修改的DOM元素設置?display:none悼瘾,操作完再顯示。(因為隱藏元素不在render樹內审胸,因此修改隱藏元素不會觸發(fā)回流重繪)

避免多次讀取某些屬性(見上)

將復雜的節(jié)點元素脫離文檔流亥宿,降低回流成本

為什么一再強調將css放在頭部,將js文件放在尾部

DOMContentLoaded 和 load

DOMContentLoaded 事件觸發(fā)時砂沛,僅當DOM加載完成烫扼,不包括樣式表,圖片...

load 事件觸發(fā)時碍庵,頁面上所有的DOM映企,樣式表悟狱,腳本,圖片都已加載完成

CSS 資源阻塞渲染

構建Render樹需要DOM和CSSOM堰氓,所以HTML和CSS都會阻塞渲染挤渐。所以需要讓CSS盡早加載(如:放在頭部),以縮短首次渲染的時間双絮。

JS 資源

阻塞瀏覽器的解析浴麻,也就是說發(fā)現(xiàn)一個外鏈腳本時,需等待腳本下載完成并執(zhí)行后才會繼續(xù)解析HTML囤攀。

這和之前文章提到的瀏覽器線程有關软免,瀏覽器中js引擎線程和渲染線程是互斥的,詳見《從setTimeout-setInterval看JS線程》

普通的腳本會阻塞瀏覽器解析焚挠,加上defer或async屬性膏萧,腳本就變成異步,可等到解析完畢再執(zhí)行宣蔚。

async異步執(zhí)行向抢,異步下載完畢后就會執(zhí)行,不確保執(zhí)行順序胚委,一定在onload前挟鸠,但不確定在DOMContentLoaded事件的前后

defer延遲執(zhí)行,相對于放在body最后(理論上在DOMContentLoaded事件前)

舉個栗子

<html>

?<head>

? ?<meta name="viewport" content="width=device-width,initial-scale=1">

? ?<link href="style.css" rel="stylesheet">

?</head>

?<body>

? ?<p>Hello <span>web performance</span> students!</p>

? ?<div><img src="awesome-photo.jpg"></div>

? ?<script src="app.js"></script>

?</body>

</html>

瀏覽器拿到HTML后亩冬,從上到下順序解析文檔

此時遇到css艘希、js外鏈,則同時發(fā)起請求

開始構建DOM樹

這里要特別注意硅急,由于有CSS資源覆享,CSSOM還未構建前,會阻塞js(如果有的話)

無論JavaScript是內聯(lián)還是外鏈营袜,只要瀏覽器遇到?script?標記撒顿,喚醒?JavaScript解析器,就會進行暫停?blocked?瀏覽器解析HTML荚板,并等到?CSSOM?構建完畢凤壁,才執(zhí)行js腳本

渲染首屏(DOMContentLoaded 觸發(fā),其實不一定是首屏跪另,可能在js腳本執(zhí)行前DOM樹和CSSOM已經構建完render樹拧抖,已經paint)

首屏優(yōu)化Tips

說了這么多,其實可以總結幾點瀏覽器首屏渲染優(yōu)化的方向:

減少資源請求數(shù)量(內聯(lián)亦或是延遲動態(tài)加載)

使CSS樣式表盡早加載免绿,減少@import的使用唧席,因為需要解析完樣式表中所有import的資源才會算CSS資源下載完

異步js:阻塞解析器的 JavaScript 會強制瀏覽器等待 CSSOM 并暫停 DOM 的構建,導致首次渲染的時間延遲

so on...

知道操作DOM成本多高了嗎?

其實寫了這么多,感覺偏題了淌哟,大量的資料參考的是chrome開發(fā)者文檔迹卢。感覺js腳本資源那塊還是有點亂,包括和DOMContentLoaded的關系徒仓,希望大家能多多指點婶希,多多批評,謝謝大佬們蓬衡。

操作DOM具體的成本喻杈,說到底是造成瀏覽器回流reflow和重繪reflow,從而消耗GPU資源狰晚。

參考文獻

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末筒饰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子壁晒,更是在濱河造成了極大的恐慌瓷们,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秒咐,死亡現(xiàn)場離奇詭異谬晕,居然都是意外死亡,警方通過查閱死者的電腦和手機携取,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門攒钳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雷滋,你說我怎么就攤上這事不撑。” “怎么了晤斩?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵焕檬,是天一觀的道長。 經常有香客問我澳泵,道長实愚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任兔辅,我火速辦了婚禮腊敲,結果婚禮上,老公的妹妹穿的比我還像新娘幢妄。我一直安慰自己兔仰,他們只是感情好茫负,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布蕉鸳。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪潮尝。 梳的紋絲不亂的頭發(fā)上榕吼,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機與錄音勉失,去河邊找鬼羹蚣。 笑死,一個胖子當著我的面吹牛乱凿,可吹牛的內容都是我干的顽素。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼徒蟆,長吁一口氣:“原來是場噩夢啊……” “哼胁出!你這毒婦竟也來了?” 一聲冷哼從身側響起段审,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤全蝶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寺枉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抑淫,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年姥闪,在試婚紗的時候發(fā)現(xiàn)自己被綠了始苇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡筐喳,死狀恐怖埂蕊,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情疏唾,我是刑警寧澤蓄氧,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站槐脏,受9級特大地震影響喉童,放射性物質發(fā)生泄漏。R本人自食惡果不足惜顿天,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一堂氯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牌废,春花似錦咽白、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽排抬。三九已至,卻和暖如春授段,著一層夾襖步出監(jiān)牢的瞬間蹲蒲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工侵贵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留届搁,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓窍育,卻偏偏與公主長得像卡睦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子漱抓,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內容

  • 從我接觸前端到現(xiàn)在辽旋,一直聽到的一句話:操作DOM的成本很高浩嫌,不要輕易去操作DOM。尤其是React补胚、vue等MV*...
    流動碼文閱讀 540評論 1 2
  • 從我接觸前端到現(xiàn)在溶其,一直聽到的一句話:操作DOM的成本很高骚腥,不要輕易去操作DOM。尤其是React瓶逃、vue等MV*...
    DoEmpty閱讀 343評論 0 0
  • 瀏覽器如何渲染網頁 要了解瀏覽器渲染頁面的過程束铭,首先得知道一個名詞——關鍵渲染路徑。關鍵渲染路徑是指瀏覽器從最初接...
    oWSQo閱讀 32,898評論 5 74
  • 發(fā)送 & 接收信息 數(shù)據(jù)是以“數(shù)據(jù)包”的形式通過互聯(lián)網發(fā)送厢绝,而數(shù)據(jù)包以字節(jié)為單位契沫。當你編寫一些 HTML、CSS ...
    IT界中小PQ閱讀 405評論 0 0
  • 發(fā)送 & 接收信息 數(shù)據(jù)是以“數(shù)據(jù)包”的形式通過互聯(lián)網發(fā)送昔汉,而數(shù)據(jù)包以字節(jié)為單位懈万。當你編寫一些 HTML、CSS ...
    mongofeng閱讀 909評論 0 0