在Web開發(fā)中,JavaScript的一個(gè)很重要的作用就是對DOM進(jìn)行操作,可你知道么?對DOM的操作是非常昂貴的,因?yàn)檫@會(huì)導(dǎo)致瀏覽器執(zhí)行回流操作,而執(zhí)行了過多的回流操作,你就會(huì)發(fā)現(xiàn)自己的網(wǎng)站變得越來越慢了,我們應(yīng)該盡可能的減少DOM操作。本文是這個(gè)系列的最后一篇,給出了一些指導(dǎo)性原則,比如在什么時(shí)候應(yīng)該對DOM可以進(jìn)行什么樣的操作等。
【原文】Nicholas C. Zakas - Speed up your JavaScript, Part 4 【譯文】明達(dá) - 如何提升JavaScript的運(yùn)行速度(DOM篇)
以下是對原文的翻譯:
在過去的幾周中,我為大家介紹了幾種可以加快JavaScript腳本運(yùn)行速度的技術(shù)。第一節(jié) 介紹了如何優(yōu)化循環(huán)。第二節(jié) 的重點(diǎn)放在優(yōu)化函數(shù)內(nèi)部代碼上,還介紹了隊(duì)列(queuing)和記憶化(memoization)兩種技術(shù),來減輕函數(shù)的工作負(fù)擔(dān)。第三節(jié) 就如何將遞歸轉(zhuǎn)換為迭代循環(huán)或者記憶化方式的話題,展開了討論。第四節(jié)是這個(gè)系列的最后一篇,也就是本文,將重點(diǎn)闡述過多的DOM操作所帶來的影響。
我們都知道,DOM操作的效率是很低的,而且不是一般的慢,而且這也是引發(fā)性能問題的常見問題之一。為什么會(huì)慢呢?因?yàn)閷OM的修改為影響網(wǎng)頁的用戶界面,重繪頁面是一項(xiàng)昂貴的操作。太多的DOM操作會(huì)導(dǎo)致一系列的重繪操作,為了確保執(zhí)行結(jié)果的準(zhǔn)確性,所有的修改操作是按順序同步執(zhí)行的。我們稱這個(gè)過程叫做回流(reflow),同時(shí)這也是最昂貴的瀏覽器操作之一。回流操作主要會(huì)發(fā)生在幾種情況下:
- * 當(dāng)對DOM節(jié)點(diǎn)執(zhí)行新增或者刪除操作時(shí)。
- * 動(dòng)態(tài)設(shè)置一個(gè)樣式時(shí)(比如element.style.width="10px")。
- * 當(dāng)獲取一個(gè)必須經(jīng)過計(jì)算的尺寸值時(shí),比如訪問offsetWidth、clientHeight或者其他需要經(jīng)過計(jì)算的CSS值(在兼容DOM的瀏覽器中,可以通過getComputedStyle函數(shù)獲取;在IE中,可以通過currentStyle屬性獲。。
解決問題的關(guān)鍵,就是限制通過DOM操作所引發(fā)回流的次數(shù)。大部分瀏覽器都不會(huì)在JavaScript的執(zhí)行過程中更新DOM。相應(yīng)的,這些瀏覽器將對對DOM的操作放進(jìn)一個(gè)隊(duì)列,并在JavaScript腳本執(zhí)行完畢以后按順序一次執(zhí)行完畢。也就是說,在JavaScript執(zhí)行的過程中,用戶不能和瀏覽器進(jìn)行互動(dòng),直到一個(gè)回流操作被執(zhí)行。( 失控腳本對話框 會(huì)觸發(fā)回流操作,因?yàn)樗麍?zhí)行了一個(gè)中止JavaScript執(zhí)行的操作,此時(shí)會(huì)對用戶界面進(jìn)行更新)
如果要減少由于DOM修改帶來的回流操作,有兩個(gè)基本的方法。第一個(gè)就是在對當(dāng)前DOM進(jìn)行操作之前,盡可能多的做一些準(zhǔn)備工作。一個(gè)經(jīng)典的例子就是向document對象中添加很多DOM節(jié)點(diǎn):
for (var i=0; i < items.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); list.appendChild(item); }
這段代碼的效率是很低的,因?yàn)樗诿看窝h(huán)中都會(huì)修改當(dāng)前DOM結(jié)構(gòu)。為了提高性能,我們需要將這個(gè)次數(shù)降到最低,對于這個(gè)案例來說,最好的辦法是建立一個(gè)文檔碎片(document fragment),作為那些已創(chuàng)建元素元素的臨時(shí)容器,最后一次將容器的內(nèi)容直接添加到父節(jié)點(diǎn)中:
var fragment = document.createDocumentFragment(); for (var i=0; i < items.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); fragment.appendChild(item); } list.appendChild(fragment);
經(jīng)過調(diào)整的代碼,只會(huì)修改一次當(dāng)前DOM的結(jié)構(gòu),就在最后一行,而在這之前,我們用文檔碎片來保存那些中間結(jié)果。因?yàn)槲臋n碎片沒有任何可見內(nèi)容,所以這類修改不會(huì)觸發(fā)回流操作。實(shí)際上,文檔碎片也不能被添加到DOM中,我們需要將它作為參數(shù)傳給appendChild函數(shù),而實(shí)際上添加的不是文檔碎片本身,而是它下面的所有子元素。
出處:七月佑安
責(zé)任編輯:bluehearts
上一頁 下一頁 提升JavaScript運(yùn)行速度之DOM篇 [2]
◎進(jìn)入論壇網(wǎng)頁制作、WEB標(biāo)準(zhǔn)化版塊參加討論,我還想發(fā)表評論。
|