摘要:刨根問底,這里說的成本,到底高在哪兒呢什么是文檔對象模型什么是可能很多人第一反應就是等標簽至少我是,但要知道,是,是,對象模型,是為提供的。操作具體的成本,說到底是造成瀏覽器回流和重繪,從而消耗資源。
從我接觸前端到現(xiàn)在,一直聽到的一句話:操作DOM的成本很高,不要輕易去操作DOM。尤其是React、vue等MV*框架的出現(xiàn),數(shù)據(jù)驅(qū)動視圖的模式越發(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里面的內(nèi)容。但在瀏覽器端,前端們可以用腳本語言(JavaScript)通過DOM去操作HTML內(nèi)容。
那么問題來了,只有JavaScript才能調(diào)用DOM這個API嗎?
答案是NO。
Python也可以訪問DOM。所以DOM不是提供給Javascript的API,也不是Javascript里的API。
PS: 實質(zhì)上還存在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樹Critical Path Hello web performance students!
無論是DOM還是CSSOM,都是要經(jīng)過Bytes → characters → tokens → nodes → object model這個過程。
DOM樹構建過程:當前節(jié)點的所有子節(jié)點都構建好后才會去構建當前節(jié)點的下一個兄弟節(jié)點。2.構建CSSOM樹
上述也提到了CSSOM的構建過程,也是樹的結構,在最終計算各個節(jié)點的樣式時,瀏覽器都會先從該節(jié)點的普遍屬性(比如body里設置的全局樣式)開始,再去應用該節(jié)點的具體屬性。還有要注意的是,每個瀏覽器都有自己默認的樣式表,因此很多時候這棵CSSOM樹只是對這張默認樣式表的部分替換。
3.生成render樹DOM樹和CSSOM樹合并生成render樹
簡單描述這個過程:
DOM樹從根節(jié)點開始遍歷可見節(jié)點,這里之所以強調(diào)了“可見”,是因為如果遇到設置了類似display: none;的不可見節(jié)點,在render過程中是會被跳過的(但visibility: hidden; opacity: 0這種仍舊占據(jù)空間的節(jié)點不會被跳過render),保存各個節(jié)點的樣式信息及其余節(jié)點的從屬關系。
4.Layout 布局有了各個節(jié)點的樣式信息和屬性,但不知道各個節(jié)點的確切位置和大小,所以要通過布局將樣式信息和屬性轉(zhuǎn)換為實際可視窗口的相對大小和位置。
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和repaintreflow(回流): 根據(jù)Render Tree布局(幾何屬性),意味著元素的內(nèi)容、結構、位置或尺寸發(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、調(diào)用了getComputedStyle()或者IE的currentStyle
reflow回流必定引起repaint重繪,重繪可以多帶帶觸發(fā)
背景色、顏色、字體改變(注意:字體大小發(fā)生變化時,會觸發(fā)回流)
優(yōu)化reflow、repaint觸發(fā)次數(shù)避免逐個修改節(jié)點樣式,盡量一次性修改
使用DocumentFragment將需要多次修改的DOM元素緩存,最后一次性append到真實DOM中渲染
可以將需要多次修改的DOM元素設置display: none,操作完再顯示。(因為隱藏元素不在render樹內(nèi),因此修改隱藏元素不會觸發(fā)回流重繪)
避免多次讀取某些屬性(見上)
將復雜的節(jié)點元素脫離文檔流,降低回流成本
為什么一再強調(diào)將css放在頭部,將js文件放在尾部 DOMContentLoaded 和 loadDOMContentLoaded 事件觸發(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事件前)
舉個栗子Hello web performance students!
瀏覽器拿到HTML后,從上到下順序解析文檔
此時遇到css、js外鏈,則同時發(fā)起請求
開始構建DOM樹
這里要特別注意,由于有CSS資源,CSSOM還未構建前,會阻塞js(如果有的話)
無論JavaScript是內(nèi)聯(lián)還是外鏈,只要瀏覽器遇到 script 標記,喚醒JavaScript解析器,就會進行暫停 blocked 瀏覽器解析HTML,并等到 CSSOM 構建完畢,才執(zhí)行js腳本
渲染首屏(DOMContentLoaded 觸發(fā),其實不一定是首屏,可能在js腳本執(zhí)行前DOM樹和CSSOM已經(jīng)構建完render樹,已經(jīng)paint)
首屏優(yōu)化Tips說了這么多,其實可以總結幾點瀏覽器首屏渲染優(yōu)化的方向
減少資源請求數(shù)量(內(nèi)聯(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/
已同步至個人博客-軟硬皆施
Github 歡迎star :)
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/93802.html
摘要:刨根問底,這里說的成本,到底高在哪兒呢什么是文檔對象模型什么是可能很多人第一反應就是等標簽至少我是,但要知道,是,是,對象模型,是為提供的。操作具體的成本,說到底是造成瀏覽器回流和重繪,從而消耗資源。 從我接觸前端到現(xiàn)在,一直聽到的一句話:操作DOM的成本很高,不要輕易去操作DOM。尤其是React、vue等MV*框架的出現(xiàn),數(shù)據(jù)驅(qū)動視圖的模式越發(fā)深入人心,jQuery時代提供的強大便...
摘要:回流重繪及其優(yōu)化渲染過程渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容解析抽象抽象出布局繪畫抽象渲染引擎的第一步是解析文檔并將解析的元素轉(zhuǎn)換為樹中的實際節(jié)點。 回流、重繪及其優(yōu)化 渲染過程 渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容 解析HTML抽象DOM tree 抽象出Render tree 布局(layout)render tree 繪畫render tree 抽象DOM tree 渲染引擎...
摘要:回流重繪及其優(yōu)化渲染過程渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容解析抽象抽象出布局繪畫抽象渲染引擎的第一步是解析文檔并將解析的元素轉(zhuǎn)換為樹中的實際節(jié)點。 回流、重繪及其優(yōu)化 渲染過程 渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容 解析HTML抽象DOM tree 抽象出Render tree 布局(layout)render tree 繪畫render tree 抽象DOM tree 渲染引擎...
摘要:回流重繪及其優(yōu)化渲染過程渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容解析抽象抽象出布局繪畫抽象渲染引擎的第一步是解析文檔并將解析的元素轉(zhuǎn)換為樹中的實際節(jié)點。 回流、重繪及其優(yōu)化 渲染過程 渲染引擎通過通過網(wǎng)絡請求接收渲染內(nèi)容 解析HTML抽象DOM tree 抽象出Render tree 布局(layout)render tree 繪畫render tree 抽象DOM tree 渲染引擎...
摘要:如果與相關人員的溝通不足,將很容易造成各做各事重復勞動,甚至造成不必要的損失。所以在項目管理上,項目經(jīng)理不得不花費大量的精力在溝通上。 偶然回想起中學時候一次過生日,邀請朋友來家里,突然我就開始糾結到底要不要去買蛋糕。要是買,他在來的時候也買一個該怎么辦……我是不是應該打個電話給他呢?可是打了我說啥呢,說你到哪兒了,你買蛋糕了嗎?莫名感覺有點尷尬……showImg(https://im...
閱讀 2635·2021-11-18 10:07
閱讀 1089·2021-08-03 14:04
閱讀 731·2019-08-30 13:08
閱讀 2585·2019-08-29 15:33
閱讀 1099·2019-08-29 14:07
閱讀 2997·2019-08-29 14:04
閱讀 1447·2019-08-29 11:19
閱讀 1152·2019-08-29 10:59