摘要:思路合并所有改變然后一次性處理使用屬性修改類名批量修改當你需要對元素進行一系列操作的時候,不妨按照如下步驟使元素脫離文檔流對其應用多重改變把元素帶回文檔中上面的這一套組合拳中,第一步和第三部分別會觸發一次重排。
前言:隨著vue,react, angular的流行,可能現在我們不必經常的操作DOM,三大框架在副交互的操作中發揮著極大地優勢。因為我們知道用腳本對DOM的操作非常昂貴,本文主要探討常規的DOM操作中你可能不知道的知識。
瀏覽器中的DOM首先,我們來了解一下什么是DOM,他為什么慢?
DOM,天生就慢文檔對象模型 (DOM)是 W3C(萬維網聯盟)的標準,是一個獨立于語言的,用于操作XML和HTML文檔的程序接口。在瀏覽器中,主要和HTML文檔打交道,盡管DOM是個與語言無關的API,但是它在瀏覽器中的接口卻是用JS實現的。但是通常瀏覽器會把DOM和JS分開來實現,比如Chrome中的DOM實現為webkit中的webCore,但js引擎是Google自己研發的V8。既然分開了,一旦兩者需要產生連接,就要付出代價。
DOM 訪問與修改前面說到JS訪問DOM會產生而性能上的消耗,如果去修改DOM元素則代價更為昂貴,因為它會導致瀏覽器重新計算頁面的幾何變化。 來看兩端段代碼:
第一段代碼定義了一個普通的變量,進行循環累加,執行事件幾乎為。第二段代碼,每次循環會訪問兩次特定的元素:第一次讀取這個元素的innerHTML屬性,第二次重寫它。可以看到它的執行時間將近2.8s,這段腳本會一直阻塞后續的執行。
看清楚了這一點,不難得到一個效率更高的版本:
用一個局部變量包層每次更新后的內容,等待循環結束后,一次性的寫入頁面(盡可能的把更多的工作交給js的部分來做)。可以看到快了近2000倍。
顯而易見。訪問DOM的次數越多,代碼的運行速度越慢,因此,通用的經驗做法是:減少訪問DOM的次數,把運算盡量留給JS處理。這也是Vue或者React為什么要設計virtual dom 的初衷之一:把負責的DOM模型映射成JS對象,不去直接操作DOM而是通過操作JS對象,最后再進行 innerHtml 或者 append。
我們先來看一段代碼:
var alldivs = document.getElementsByTagName("div"); for (var i = 0; i < alldivs.length; i++){ document.body.appendChild(document.createElement("div")); }
乍一看,這段代碼只是單純的把頁面中的div數量翻倍:遍歷所有的div,每次創建一個新的div并創建到添加到body中。
但事實上,這是一個死循環:因為循環的退出條件alldivs.length在每一次循環結束后都會增加,因為這個HTML元素集合反映的是底層文檔元素的實時狀態。 類似于下面的HTML集合其實都會存在這樣的問題:
document.getElementsByTagName()
document.getElementsByClassName()
document.getElementsByName()
....
其次上面的代碼也會存在一個性能問題,就是每次都需要訪問DOM對象的length屬性。其實我們也可以用一個變量緩存起來對象的length屬性:
var len = document.getElementsByTagName("div").length; for (var i = 0; i < len; i++){ document.body.appendChild(document.createElement("div")); }重繪與重排
瀏覽器用來顯示頁面的所有“組件”,有:HTML標簽、js、css、圖片——之后會解析并生成兩個內部的數據結構:
DOM樹(表示頁面結構)
渲染樹(表示DOM節點應該如何表示)
DOM樹中的每一個需要顯示的節點在渲染樹中至少存在一個對應的節點。
渲染樹中的節點被稱為“幀(frames)”或“盒(boxes)”,符合css盒模型的定義,理解頁面元素為一個具有padding、margin、borders和position的盒子。
一旦渲染樹構建完成,瀏覽器就開始顯示頁面元素,這個過程稱為繪制(paint)。
當DOM的變化影響了元素的幾何屬性(寬、高)——比如改變改變了邊框的寬度或者給一個段落增加一些文字導致其行數的增加——瀏覽器就需要重新計算元素的幾何屬性,同樣,頁面中其他元素的幾何屬性和位置也會因此受到影響。
瀏覽器會使渲染樹中收到影響的部分消失,重新構建渲染樹,這個過程稱為“重排(reflow)”。重排完成之后,瀏覽器會重新將受到影響的部分繪制到瀏覽器中,這個過程稱之為“重繪(repaint)”。
如果改變的不是元素的幾何屬性,如:改變元素的背景顏色,不會發生重排,只會發生一次重繪,因為元素的布局并沒有改變。
不管是重繪還是重排,都是代價昂貴的操作,它們會導致web應用程序的UI反應遲鈍,應當盡可能的減少這類過程的發生。
重排何時發生?
添加或刪除可見的DOM元素
元素位置的改變
元素尺寸的改變(padding、margin、border、height、width)
內容改變(文本改變或圖片尺寸改變)
頁面渲染器初始化
瀏覽器窗口尺寸改變
滾動條的出現(會觸發整個頁面的重排)
改變樣式
一個栗子:
var el = document.getElementById("mydiv"); el.style.borderLeft = "1px"; el.style.borderRight = "2px"; el.style.padding = "5px";
示例中,元素的三個樣式被改變,而且每一個都會影響元素的幾何結構。在最糟糕的情況下,這段代碼會觸發三次重排(大部分現代瀏覽器為此做了優化,只會觸發一次重排)。從另一個角度看,這段代碼四次訪問DOM,可以被優化。
var el = document.getElementById("mydiv"); //思路:合并所有改變然后一次性處理 //method_1:使用cssText屬性 el.style.cssText = "border-left: 1px; border-right: 2px; padding: 5px"; //method_2:修改類名 el.className = "anotherClass";
當你需要對DOM元素進行一系列操作的時候,不妨按照如下步驟:
使元素脫離文檔流
對其應用多重改變
把元素帶回文檔中
上面的這一套組合拳中,第一步和第三部分別會觸發一次重排。但是如果你忽略了這兩個步驟,那么在第二步所產生的任何修改都會觸發一次重排。
在此安利三種可以使DOM元素脫離文檔流的方法:
隱藏元素
使用文檔片段(document fragment)在當前DOM之外構建一個子樹,再把它拷貝回文檔
將原始元素拷貝到一個脫離文檔的節點中,修改副本,完成后再替換原始元素
一般情況下,重排只影響渲染樹中的一小部分,但也可能影響很大的一部分,甚至是整個渲染樹。
瀏覽器所需的重排次數越少,應用程序的響應速度也就越快。
想象這樣一種情況,頁面的底部有一個動畫,會推移頁面整個余下的部分,這將是一次代價昂貴的大規模重排!用戶也勢必會感覺到頁面一卡一卡的。
因此,使用以下步驟可以避免頁面中的大部分重排:
使用絕對定位讓頁面上的動畫元素脫離文檔流
動畫展示階段
動畫結束時,將元素恢復定位。
從IE7開始,IE允許在任何元素上使用:hover這個css選擇器。
然而,如果你有大量元素使用了:hover,你會發現,賊喇慢!
關于
作者:monkeyWang
本人主頁:https://monkeywangs.github.io/
微信公眾號:會不定期推送前端技術文章,歡迎關注
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51478.html
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
閱讀 3640·2023-04-26 02:07
閱讀 3150·2021-09-22 15:55
閱讀 2534·2021-07-26 23:38
閱讀 3119·2019-08-29 15:16
閱讀 2008·2019-08-29 11:16
閱讀 1746·2019-08-29 11:00
閱讀 3583·2019-08-26 18:36
閱讀 3164·2019-08-26 13:32