DOM真的很慢嗎?如果真的是,為什麼不去改進它?


說DOM慢,大多數時候是指DOM操作所產生的副作用慢,而且還有很多陷阱,比如對一些屬性訪問(甚至是讀操作!!)時觸發reflow。

為什麼不去改進它呢?因為除了一些best-practice和黑科技的指導以外,DOM對於前端來說基本上還是個黑盒,再加上那麼多瀏覽器不按spec做,真是信誰都不成了。


相對於 Object/Array 操作,DOM 確實是慢上幾個數量級。現代瀏覽器已經對 DOM操作 做了無數優化,現在用不用 document fragment 在性能上已經沒有差別。

以上結論可以看這個測試: dom vs array manipulation 路 jsPerf

但相比起 DOM 操作來說, 渲染(包括layout、paint和composite)更慢,單單優化 DOM 操作性能只是減少 js 執行時間而已,而 Web 性能的瓶頸通常在渲染上。當你的網頁出現性能問題時,請使用 Chrome devtools 來幫助定位原因。

對性能要求很高的場景,例如網頁遊戲、複雜動畫等,可以考慮 HTML5 提供的 2d canvas 或 webgl,它們使用了更底層的繪圖api,性能會有指數級的提高。


我在市中心擺攤,但是要回縣城運貨,而且只有我一個人,能不慢嗎?

不同的執行域卻是同一線程,優化也就是縣城和市中心修了條高速而已


頻繁的dom操作,相比確實影響性能。在開發過程中應盡量少的引起重繪和重排,比如使用createDocumentFragment方法創建dom片段,一次性處理。

目前reactjs就是讓開發者構建vitual DOM,隨後react自己真正更新dom,這樣有效避免了dom的頻繁操作。

至於dom的改進,這需要一個過程,現在也有react、canvas輸出等優化方案,以後也許會有更好更多的方案替代dom方案。


其實DOM載入並不慢,慢的是layout,瀏覽器經常性需要確定哪些地方需要重繪,當js把控制權交給瀏覽器的時候,他會調用layout演算法,更準確地說就它會調用 CSS 重算演算法,然後layout、然後重繪、重新組合。layout 在處理某些屬性時,是同步觸發的。比如什麼

.offsetWidth .offsetHeight

不過確實,現在瀏覽器越來越牛逼了,很多重繪也就不算什麼了。


DOM 慢是因為考慮的問題多,隨著標準的提出,只會越來越慢,但是瀏覽器引擎和計算機越來越快了


現在有shadow DOM了


DOM是給JS操作HTML及XML文檔提供了API,兩個相互獨立的功能只要通過介面彼此連接,就會產生消耗。

而且它在瀏覽器中是以JavaScript實現的,JS本身的執行效率就低於JAVA或者C(JS是解釋型語言,邊編譯邊執行;JAVA或者C是編譯型語言,一次編譯,直接執行),所以DOM有點慢。

  • 在訪問與修改DOM時開銷很大,特別是修改元素時,會導致瀏覽器重新渲染頁面。所以,要盡量減少修改次數,可以多次累積一次修改。在獲取HTML元素集合時,也是非常耗能的,因為動態查詢的,隨時反映元素狀態,多次使用元素集合時應緩存為局部變數,以減少查詢次數。
  • 如樓上知友所說,重繪與重排開銷也非常大。特別是重排,在DOM的變化影響了元素的幾何屬性時,瀏覽器會重新計算元素的幾何屬性,其他相關元素也會受到影響,受到影響的元素就會被重新構造渲染樹。完成重排之後,瀏覽器會重新繪製受影響的元素到屏幕中,也就是重繪。每次重排都是非常耗能的,瀏覽器通過隊列化修改並批量的執行來優化重排過程。然而,獲取這些布局信息的時候(offsetTop,scrollTop,clientTop等)會導致隊列刷新,也就是立即執行,不能完全的批量執行。所以,在需要獲取這些布局信息的時候,可以放在批量重排之後。要想提高性能,也需要最小化重排和重繪。
  • 如果頁面上元素很多,而且很多元素上也綁定有事件處理程序,也會加重瀏覽器負擔。事件綁定越多,由於瀏覽器要跟蹤每個事件處理器,也會佔用更多的內存。可以用事件委託,減少大量的事件綁定,減輕瀏覽器的壓力。

至於改進嘛,個人感覺按照現在這個趨勢就行。隨著DOM0、DOM2、DOM3公布,新的API也在不斷增加,雖然這些新的API中有的會帶來性能問題,但給我們提供了極大的便利。其實,隨著硬體水平的快速發展,很多問題將不再是問題。


都說dom很慢,那到底哪裡慢了呢??

  1. dom 的屬性很多, 還有事件, 方法等..創建dom節點的消耗大嗎?? (時間單位為: ms)

圖1

如上圖1所示: 創建10000個div節點到body上, 還有layout 的時間都是可以接受的, 才幾毫秒(js的date() 計時不是很准,只看數量級就好了)

2. 在1. 中貌似dom操作還是可以接受的. 其中div是平鋪到body上的, 那如果是遞歸嵌套的呢??

遞歸嵌套: 例如: a,b,c都是節dom點, 不停的嵌套就是: a.append(b) , b.append(c) ... 以下是一萬個節點的試驗:

圖2

如上圖2所示: 光是js 的操作就1s 多了, layout還要1s多, 好慢呀

3. 為啥嵌套的append 要慢這麼多??

直接猜測: 平鋪append 的時間複雜度為 O(n), 那麼 嵌套的append 的時間複雜度為 O(n^2)

輔證猜測: append 的操作 很有可能遍歷了 當前的節點鏈 , 就像是1 個 , 2個 ..... n個相加 的時間複雜度

疑問: 普通的鏈表數據結構在新插入節點時複雜度為O(1), 為啥dom鏈的是O(n)呢?? 也就是哪裡使得他要遍歷當前鏈已有的節點了??

最終揭秘: dom節點的append 操作時會檢查dom樹上是否有重複的節點, 具體有以下表現:

  1. 在dom樹上的某一個節點append另一個分支上的節點時會發生啥呢? : 節點會發生移動. 普通的link也這樣, 這個不需要檢查是否重複, 只是引用的改變就可以了
  2. 可以構造循環的dom節點嗎?? 不行直接報錯: 貌似防止循環鏈表只能遍歷了.

4. 為啥嵌套的layout 也這麼慢?? 這個是盒子模型的問題: 外面的是被裡面的撐開的, 裡層多一個外面的都要重新計算. 那為啥不從裡面往外算? 這樣只是解決了大小問題, 但是位置呢? 還是很煩..

5. 不要隨便嵌套標籤呀, 很影響性能的... 如果嵌套多的話html的載入不咋影響(瀏覽器內部可優化) 但是 layout 的速度就沒法了...


考慮的情況越多,代碼越多邏輯越複雜,性能也就越差


dom和js之間有一座橋。每次交流都會過一次橋。


嗯 改進啊 都用canvas吧。


dom是真正的遷一發動全身,尤其新特性多了之後,布局越來越複雜,bug也是越來越多,當然也成就了了很直觀ui構建技術.


推薦閱讀:

為什麼DOM不提供insertAfter()方法?
children.length和childElementCount?
如何理解js高程里的document對象是HTMLDocument的實例?

TAG:前端開發 | HTML | JavaScript | 前端工程師 | DOM |