為什麼說 DOM 綁定事件很耗性能?

手動寫了個例子, 跟操作 dataset 作對比, 好像也不是很慢啊, 大家說它慢是為啥?

https://gist.github.com/jiyinyiyong/0e8f01cb387976cbe1ac3bd948bcd772


不知道你是從哪裡聽說 DOM 綁定事件很慢的(一定有人在造謠),然而其實 DOM 並不慢,真正慢的是導致瀏覽器重排的 DOM 操作。有下面的基準測試為證:

Binding Events For DOM

稍微會比訪問 dataset 慢一些,但是比訪問 innerHTML 要快得多。

從 JavaScript 中訪問 DOM 其實確實會導致一定的性能損耗,因為 DOM 本質上不屬於 JavaScript 的內容,而是一組橋接 document 和 JavaScript 世界的橋樑。但是對 DOM 這種 API 訪問的損耗其實可以忽略不計。大家覺得 DOM 很慢,其實是因為不恰當地訪問 DOM 可能會導致瀏覽器的重排,這個才是性能的真正殺手!所以你可以看到上面對 innerHTML 訪問的性能是最差的,因為它導致瀏覽器的重排。

當你修改 innerHTML (或者任何能改變 DOM 元素的幾何結構)的時候,瀏覽器需要重新計算部分甚至是整個頁面的幾何結構信息。瀏覽器需要遍歷 DOM 樹,根據 CSS 規則進行對受到影響的 DOM 元素進行計算,然後再進行重新繪製。所以你稍微動一下 DOM,瀏覽器可能就要吭哧吭哧地進行大量的計算。

在瀏覽器優化升級了這麼多代的今天,其實DOM 並沒有傳言的那麼慢,真正慢的是導致瀏覽器重排的 DOM 操作。再重複一遍:DOM 並不慢,真正慢的是導致瀏覽器重排的 DOM 操作。

綁定 DOM 事件這種操作,其實壓根就不會改變元素的幾何位置,並不會導致瀏覽器重排。所以性能損耗其實並不會很大。

再補充一點乾貨,什麼 DOM 操作會導致瀏覽器重排?這些 DOM 訪問、操作是需要我們時刻留意的:

elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent

elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight

elem.getClientRects(), elem.getBoundingClientRect()

elem.scrollBy(), elem.scrollTo()

elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()

elem.scrollWidth, elem.scrollHeight

elem.scrollLeft, elem.scrollTop

elem.focus()

elem.computedRole, elem.computedName

elem.innerText

window.getComputedStyle()

window.scrollX, window.scrollY

window.innerHeight, window.innerWidth

window.getMatchedCSSRules()

mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY

doc.scrollingElement

range.getClientRects(), range.getBoundingClientRect()

...

待續


你這個 benchmark 也太不科學了,裡面什麼 .createElement() .innerText 都有,噪音和開銷都太大。

要測性能,首先得明確 case , case 沒說清楚,不如不測。


DOM綁定並不耗性能,濫用DOM綁定才耗性能。

假設一個div內有100個button,現在需要使每個button點擊時alert其序號。如果我們遍歷這100個button,給他們一個個綁定上onclick事件,那麼瀏覽器就會針對每個button創建一個引用,這100個引用指向100個匿名函數。在現在的計算機上執行這段代碼我可以說並沒太大的性能問題,但如果是1000個,10000個button呢?

這時候就要應用到事件代理了,我們可以給這100個button的父元素綁定一個onclick事件,在事件中判斷點擊事件的target是否為button(舊ie判斷srcElement),如果是button,那麼再針對target執行相應的alert操作。在這裡瀏覽器只需要給div添加綁定一次事件即可實現我們的需求,在內存消耗上要比之前的方法小得多,也避免了很多潛在的內存泄露問題。

DOM綁定在現代web開發中已經是不可或缺的了,只要有交互必然需要用到DOM綁定,我們需要做的是合理運用事件代理,避免濫用綁定造成應用性能下降。可能的話可以用現在各種流行框架:angular,vue,react等,這些框架本身會一定程度上處理dom元素的綁定,開發者無需關注底層綁定的實現。


DOM 並不慢,真正慢的是導致瀏覽器重排的 DOM 操作。

React.js 的演算法本質上是重刷視圖,相當於設置 innerHTML,只不過它設置的是內存裡面 Virtual-DOM,而不是真實的 DOM。你要是拿它和真實的 innerHTML 比那肯定是 React.js 更快。

React.js 厲害的地方並不是說它比 DOM 快(這句話本來就是錯的),而是說不管你數據怎麼變化,我都可以以最小的代價來更新 DOM。方法就是我在內存裡面用新的數據刷新一個虛擬的 DOM 樹,然後新舊 DOM 樹進行比較,找出差異,再更新到真正的 DOM 樹上。

這就是所謂的 diff 演算法,雖然說 diff 演算法號稱演算法複雜度 O(n) 可以得到最小操作結果,但實際上 DOM 樹很大的時候,遍歷兩棵樹進行各種對比還是有性能損耗的,特別是我在頂層 setState 一個簡單的數據,你就要整棵樹 walk 一遍,而真實中我可以一句 jQuery 就搞定,所以就有了 `shouldComponentUpdate` 這種東西。

React的虛擬dom只是優化,沒說一定比原聲js操作快,但是可以肯定的事,虛擬dom大多數比原生的快


因為有一個性能更優的事件代理,沒對比就沒傷害.......


別再操作dom啦,操作數據


推薦閱讀:

在DOM里 屬性節點是不是元素節點的子節點?
fibjs 對比 nodejs 有哪些優點和缺點?
ECMAScript 6 會重蹈 ECMAScript 4 的覆轍嗎?
為何大多數人和新的項目不用 TypeScript 而用 JS + 一堆輔助工具?
IDE中,選中一個變數,文檔中其它地方的該變數也會高亮,這種功能叫什麼?如何實現的?

TAG:前端開發 | JavaScript | DOM |