偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

前端頁(yè)面卡頓?或是DOM操作惹的禍,需優(yōu)化代碼

開(kāi)發(fā) 前端 開(kāi)發(fā)工具
文檔對(duì)象模型(DOM)是一個(gè)獨(dú)立 于特定語(yǔ)言的應(yīng)用程序接口。在瀏覽器中,DOM接口是以JavaScript語(yǔ)言實(shí)現(xiàn)的,通過(guò)JavaScript來(lái)操作瀏覽器頁(yè)面中的元素,這使得 DOM成為了JavaScript中重要的組成部分。

文檔對(duì)象模型(DOM)是一個(gè)獨(dú)立 于特定語(yǔ)言的應(yīng)用程序接口。在瀏覽器中,DOM接口是以JavaScript語(yǔ)言實(shí)現(xiàn)的,通過(guò)JavaScript來(lái)操作瀏覽器頁(yè)面中的元素,這使得 DOM成為了JavaScript中重要的組成部分。在富客戶端網(wǎng)頁(yè)應(yīng)用中,界面上UI的更改都是通過(guò)DOM操作實(shí)現(xiàn)的,并不是通過(guò)傳統(tǒng)的刷新頁(yè)面實(shí)現(xiàn) 的。盡管DOM提供了豐富接口供外部調(diào)用,但DOM操作的代價(jià)很高,頁(yè)面前端代碼的性能瓶頸也大多集中在DOM操作上,所以前端性能優(yōu)化的一個(gè)主要的關(guān)注 點(diǎn)就是DOM操作的優(yōu)化。DOM操作優(yōu)化的總原則是盡量減少DOM操作。

[[132313]]

在討論DOM操作的最佳性能實(shí)踐之前,先來(lái)看看DOM操作為什么會(huì)影響性能。在瀏覽器中,DOM的實(shí)現(xiàn)和ECMAScript的實(shí)現(xiàn)是分離的。比如 在IE中,ECMAScrit的實(shí)現(xiàn)在jscript.dll中,而DOM的實(shí)現(xiàn)在mshtml.dll中;在Chrome中使用WebKit中的 WebCore處理DOM和渲染,但ECMAScript是在V8引擎中實(shí)現(xiàn)的,其他瀏覽器的情況類似。所以通過(guò)JavaScript代碼調(diào)用DOM接 口,相當(dāng)于兩個(gè)獨(dú)立模塊的交互。相比較在同一模塊中的調(diào)用,這種跨模塊的調(diào)用其性能損耗是很高的。但DOM操作對(duì)性能影響最大其實(shí)還是因?yàn)樗鼘?dǎo)致了瀏覽器 的重繪(repaint)和重排(reflow)。

為了讓大家能更深刻地理解重繪和重排對(duì)性能的影響,這里需要簡(jiǎn)單敘述一下瀏覽器的渲染原理(如果想詳細(xì)了解瀏覽器的工作原理,請(qǐng)參照文章《瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘》 )。從下載文檔到渲染頁(yè)面的過(guò)程中,瀏覽器會(huì)通過(guò)解析HTML文檔來(lái)構(gòu)建DOM樹(shù),解析CSS產(chǎn)生CSS規(guī)則樹(shù)。JavaScript代碼在解析過(guò)程中, 可能會(huì)修改生成的DOM樹(shù)和CSS規(guī)則樹(shù)。之后根據(jù)DOM樹(shù)和CSS規(guī)則樹(shù)構(gòu)建渲染樹(shù),在這個(gè)過(guò)程中CSS會(huì)根據(jù)選擇器匹配HTML元素。渲染樹(shù)包括了每 個(gè)元素的大小、邊距等樣式屬性,渲染樹(shù)中不包含隱藏元素及head元素等不可見(jiàn)元素。最后瀏覽器根據(jù)元素的坐標(biāo)和大小來(lái)計(jì)算每個(gè)元素的位置,并繪制這些元 素到頁(yè)面上。重繪指的是頁(yè)面的某些部分要重新繪制,比如顏色或背景色的修改,元素的位置和尺寸并沒(méi)用改變;重排則是元素的位置或尺寸發(fā)生了改變,瀏覽器需 要重新計(jì)算渲染樹(shù),導(dǎo)致渲染樹(shù)的一部分或全部發(fā)生變化。渲染樹(shù)重新建立后,瀏覽器會(huì)重新繪制頁(yè)面上受影響的元素。重排的代價(jià)比重繪的代價(jià)高很多,重繪會(huì)影 響部分的元素,而重排則有可能影響全部的元素。如下的這些DOM操作會(huì)導(dǎo)致重繪或重排:

  • 增加、刪除和修改可見(jiàn)DOM元素

  • 頁(yè)面初始化的渲染

  • 移動(dòng)DOM元素

  • 修改CSS樣式,改變DOM元素的尺寸

  • DOM元素內(nèi)容改變,使得尺寸被撐大

  • 瀏覽器窗口尺寸改變

  • 瀏覽器窗口滾動(dòng)

可以看出,這些操作都是DOM操作中比較常見(jiàn)的?,F(xiàn)代瀏覽器會(huì)針對(duì)重排或重繪做性能優(yōu)化,比如,把DOM操作積累一批后統(tǒng)一做一次重排或重繪。但在有些情況下,瀏覽器會(huì)立即重排或重繪。比如請(qǐng)求如下的DOM元素布局信息:offsetTop/Left/Width/Height、scrollTop/Left/Width/HeightclientTop/Left/Width/Height、getComputedStyle()currentStyle。因?yàn)檫@些值都是動(dòng)態(tài)計(jì)算的,所以瀏覽器需要盡快完成頁(yè)面的繪制,然后計(jì)算返回值,從而打亂了重排或重繪的優(yōu)化。

DOM操作帶來(lái)的頁(yè)面重繪或重排是不可避免的,但可以遵循一些最佳實(shí)踐來(lái)降低由于重排或重繪帶來(lái)的影響。如下是一些具體的實(shí)踐方法:

1. 合并多次的DOM操作為單次的DOM操作

最常見(jiàn)頻繁進(jìn)行DOM操作的是頻繁修改DOM元素的樣式,代碼類似如下:

element.style.borderColor = '#f00';
element.style.borderStyle = 'solid';
element.style.borderWidth = '1px';

這種編碼方式會(huì)因?yàn)轭l繁更改DOM元素的樣式,觸發(fā)頁(yè)面多次的重排或重繪,上面介紹過(guò),現(xiàn)代瀏覽器針對(duì)這種情況有性能的優(yōu)化,它會(huì)合并DOM操作,但并不是所有的瀏覽器都存在這樣的優(yōu)化。推薦的方式是把DOM操作盡量合并,如上的代碼可以優(yōu)化為:

// 優(yōu)化方案1
element.style.cssText += 'border: 1px solid #f00;';
// 優(yōu)化方案2
element.className += 'empty';

示例的代碼有兩種優(yōu)化的方案,都做到了把多次的樣式設(shè)置合并為一次設(shè)置。方案2比方案1稍微有一些性能上的損耗,因?yàn)樗枰樵僀SS類。但方案2的維護(hù)性最好,這在上一章曾經(jīng)討論過(guò)。很多時(shí)候,如果性能問(wèn)題并不突出,選擇編碼方案時(shí)需要優(yōu)先考慮的是代碼的維護(hù)性。

類似的操作還有通過(guò)innerHTML接口修改DOM元素的內(nèi)容。不要直接通過(guò)此接口來(lái)拼接HTML代碼,而是以字符串方式拼接好代碼后,一次性賦值給DOM元素的innerHTML接口。

2. 把DOM元素離線或隱藏后修改

把DOM元素從頁(yè)面流中脫離或隱藏,這樣處理后,只會(huì)在DOM元素脫離和添加時(shí),或者是隱藏和顯示時(shí)才會(huì)造成頁(yè)面的重繪或重排,對(duì)脫離了頁(yè)面布局流的DOM元素操作就不會(huì)導(dǎo)致頁(yè)面的性能問(wèn)題。這種方式適合那些需要大批量修改DOM元素的情況。具體的方式主要有三種:

(1)使用文檔片段

文檔片段是一個(gè)輕量級(jí)的document對(duì)象,并不會(huì)和特定的頁(yè)面關(guān)聯(lián)。通過(guò)在文檔片段上進(jìn)行DOM操作,可以降低DOM操作對(duì)頁(yè)面性能的影響,這 種方式是創(chuàng)建一個(gè)文檔片段,并在此片段上進(jìn)行必要的DOM操作,操作完成后將它附加在頁(yè)面中。對(duì)頁(yè)面性能的影響只存在于最后把文檔片段附加到頁(yè)面的這一步 操作上。代碼類似如下:

var fragment = document.createDocumentFragment();
// 一些基于fragment的大量DOM操作
...
document.getElementById('myElement').appendChild(fragment);

(2)通過(guò)設(shè)置DOM元素的display樣式為none來(lái)隱藏元素

這種方式是通過(guò)隱藏頁(yè)面的DOM元素,達(dá)到在頁(yè)面中移除元素的效果,經(jīng)過(guò)大量的DOM操作后恢復(fù)元素原來(lái)的display樣式。對(duì)于這類會(huì)引起頁(yè)面重繪或重排的操作,就只有隱藏和顯示DOM元素這兩個(gè)步驟了。代碼類似如下:

var myElement = document.getElementById('myElement');
myElement.style.display = 'none';
// 一些基于myElement的大量DOM操作
...
myElement.style.display = 'block';

(3)克隆DOM元素到內(nèi)存中

這種方式是把頁(yè)面上的DOM元素克隆一份到內(nèi)存中,然后再在內(nèi)存中操作克隆的元素,操作完成后使用此克隆元素替換頁(yè)面中原來(lái)的DOM元素。這樣一來(lái),影響性能的操作就只是最后替換元素的這一步操作了,在內(nèi)存中操作克隆元素不會(huì)引起頁(yè)面上的性能損耗。代碼類似如下:

var old = document.getElementById('myElement');
var clone = old.cloneNode(true);
// 一些基于clone的大量DOM操作
...
old.parentNode.replaceChild(clone, old);

在現(xiàn)代的瀏覽器中,因?yàn)橛辛薉OM操作的優(yōu)化,所以應(yīng)用如上的方式后可能并不能明顯感受到性能的改善。但是在仍然占有市場(chǎng)的一些舊瀏覽器中,應(yīng)用以上這三種編碼方式則可以大幅提高頁(yè)面渲染性能。

3. 設(shè)置具有動(dòng)畫效果的DOM元素的position屬性為fixed或absolute

把頁(yè)面中具有動(dòng)畫效果的元素設(shè)置為絕對(duì)定位,使得元素脫離頁(yè)面布局流,從而避免了頁(yè)面頻繁的重排,只涉及動(dòng)畫元素自身的重排了。這種做法可以提高動(dòng) 畫效果的展示性能。如果把動(dòng)畫元素設(shè)置為絕對(duì)定位并不符合設(shè)計(jì)的要求,則可以在動(dòng)畫開(kāi)始時(shí)將其設(shè)置為絕對(duì)定位,等動(dòng)畫結(jié)束后恢復(fù)原始的定位設(shè)置。在很多的 網(wǎng)站中,頁(yè)面的頂部會(huì)有大幅的廣告展示,一般會(huì)動(dòng)畫展開(kāi)和折疊顯示。如果不做性能的優(yōu)化,這個(gè)效果的性能損耗是很明顯的。使用這里提到的優(yōu)化方案,則可以 提高性能。

4. 謹(jǐn)慎取得DOM元素的布局信息

前面討論過(guò),獲取DOM的布局信息會(huì)有性能的損耗,所以如果存在重復(fù)調(diào)用,最佳的做法是盡量把這些值緩存在局部變量中。考慮如下的一個(gè)示例:

for (var i=0; i < len; i++) {
    myElements[i].style.top = targetElement.offsetTop + i*5 + 'px';
}

如上的代碼中,會(huì)在一個(gè)循環(huán)中反復(fù)取得一個(gè)元素的offsetTop值,事實(shí)上,在此代碼中該元素的offsetTop值并不會(huì)變更,所以會(huì)存在不必要的性能損耗。優(yōu)化的方案是在循環(huán)外部取得元素的offsetTop值,相比較之前的方案,此方案只是調(diào)用了一遍元素的offsetTop值。更改后的代碼如下:

var targetTop = targetElement.offsetTop;
for (var i=0; i < len; i++) {
    myElements[i].style.top = targetTop+ i*5 + 'px';
}

另外,因?yàn)槿〉肈OM元素的布局信息會(huì)強(qiáng)制瀏覽器刷新渲染樹(shù),并且可能會(huì)導(dǎo)致頁(yè)面的重繪或重排,所以在有大批量DOM操作時(shí),應(yīng)避免獲取DOM元素 的布局信息,使得瀏覽器針對(duì)大批量DOM操作的優(yōu)化不被破壞。如果需要這些布局信息,最好是在DOM操作之前就取得??紤]如下一個(gè)示例:

var newWidth = div1.offsetWidth + 10;
div1.style.width = newWidth + 'px';

var newHeight = myElement.offsetHeight + 10; // 強(qiáng)制頁(yè)面重排
myElement.style.height = newHeight + 'px'; // 又會(huì)重排一次

根據(jù)上面的介紹,代碼在遇到取得DOM元素的信息時(shí)會(huì)觸發(fā)頁(yè)面重新計(jì)算渲染樹(shù),所以如上的代碼會(huì)導(dǎo)致頁(yè)面重排兩次,如果把取得DOM元素的布局信息提前,因?yàn)闉g覽器會(huì)優(yōu)化連續(xù)的DOM操作,所以實(shí)際上只會(huì)有一次的頁(yè)面重排出現(xiàn),優(yōu)化后的代碼如下:

var newWidth = div1.offsetWidth + 10;
var newHeight = myElement.offsetHeight + 10;

div1.style.width = newWidth + 'px';
myElement.style.height = newHeight + 'px';

5. 使用事件托管方式綁定事件

在DOM元素上綁定事件會(huì)影響頁(yè)面的性能,一方面,綁定事件本身會(huì)占用處理時(shí)間,另一方面,瀏覽器保存事件綁定,所以綁定事件也會(huì)占用內(nèi)存。頁(yè)面中 元素綁定的事件越多,占用的處理時(shí)間和內(nèi)存就越大,性能也就相對(duì)越差,所以在頁(yè)面中綁定的事件越少越好。一個(gè)優(yōu)雅的手段是使用事件托管方式,即利用事件冒 泡機(jī)制,只在父元素上綁定事件處理,用于處理所有子元素的事件,在事件處理函數(shù)中根據(jù)傳入的參數(shù)判斷事件源元素,針對(duì)不同的源元素做不同的處理。這樣就不 需要給每個(gè)子元素都綁定事件了,管理的事件綁定數(shù)量變少了,自然性能也就提高了。這種方式也有很大的靈活性,可以很方便地添加或刪除子元素,不需要考慮因 元素移除或改動(dòng)而需要修改事件綁定。示例代碼如下:

// 獲取父節(jié)點(diǎn),并添加一個(gè)click事件
document.getElementById('list').addEventListener("click",function(e) { // 檢查事件源元素 if(e.target && e.target.nodeName.toUpperCase == "LI") { // 針對(duì)子元素的處理 ...
    }
});

上述代碼中,只在父元素上綁定了click事件,當(dāng)點(diǎn)擊子節(jié)點(diǎn)時(shí),click事件會(huì)冒泡,父節(jié)點(diǎn)捕獲事件后通過(guò)e.target檢查事件源元素并做相應(yīng)地處理。

在JavaScript中,事件綁定方式存在瀏覽器兼容問(wèn)題,所以在很多框架中也提供了相似的接口方法用于事件托管。比如在jQuery中可以使用如下方式實(shí)現(xiàn)事件的托管(示例代碼來(lái)自jQuery官方網(wǎng)站):

$( "table" ).on( "click", "td", function() { $( this ).toggleClass( "chosen" );
});

 

責(zé)任編輯:王雪燕 來(lái)源: 博客園
相關(guān)推薦

2014-07-18 14:10:07

WIFI華為

2009-01-07 09:22:00

2021-09-30 22:37:01

手機(jī)內(nèi)存技術(shù)

2009-09-17 08:30:57

Windows 7隱藏菜單欄

2010-09-14 11:29:43

谷歌

2013-07-22 09:43:29

2020-08-17 17:47:30

內(nèi)存技術(shù)測(cè)試

2013-01-15 10:30:45

Windows 8Metro

2010-01-12 09:25:17

Windows 7死機(jī)系統(tǒng)特效

2009-07-18 16:43:09

光纖鏈路故障接線端面臟污

2023-12-24 22:33:32

宕機(jī)Twitter馬斯克

2009-08-01 15:47:04

網(wǎng)線故障

2012-12-12 09:57:12

Chrome負(fù)載均衡

2014-11-03 16:00:55

美國(guó)寬帶

2009-04-27 13:46:30

網(wǎng)絡(luò)管理拷貝故障

2020-12-02 06:30:52

Nginx前綴FastDFS

2024-02-02 15:21:08

工具頁(yè)面性能

2017-04-19 12:36:53

2024-08-02 16:25:10

2023-09-18 07:21:18

裝機(jī)誤區(qū)主機(jī)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)