用腳本進行DOM操作的代價很昂貴犀填,它是富web應用中最常見的性能瓶頸趁桃。
1. 瀏覽器中的DOM
瀏覽器常會將DOM和js分開存放
2. 天生就慢
將dom和js文件相互獨立存放意味著什么?
簡單理解:兩個相互獨立的功能只要通過接口彼此連接笋颤,就會產(chǎn)生消耗勤篮。
有個貼切的比喻:把dom和js各自想象為一個島嶼都毒,它們之間用收費橋梁連接。js每次訪問dom,都要途徑這座橋碰缔,并繳納“過橋費”账劲。訪問DOM的次數(shù)越多,費用也就越高。因此瀑焦,推薦的做法是盡可能減少過橋的次數(shù)腌且,努力待在js的島上。
3. DOM訪問與修改
訪問DOM元素是有代價的榛瓮,修改元素則更為昂貴铺董,因為它會導致瀏覽器重新計算頁面的幾何變化。
function innerHTMLLoop(){
for (var count = 0; count < 15000; count++){
document.getElementById('here').innerHTML += 'a';
}
}
換一種效率更高的方法
function innerHTMLLoop2(){
var content = '';
for (var count = 0; count < 15000; count++){
content += 'a';
}
document.getElementById('here').innerHTML += content;
}
4. innerHTML 對比 DOM 方法
innerHTML比原生的createElement()要更快一些榆芦。
5. 節(jié)點克隆
element.cloneNode(element表示已有節(jié)點)
6. HTML集合
HTML 集合是包含了DOM節(jié)點引用的類數(shù)組對象
以下方法的返回值就是一個類數(shù)組對象
document.getElementsByName()
document.getElementsByClassName()
document.getElementsByTagName()
document.images
document.links(所有 a 元素)
document.forms
document.form[0].elements
事實上柄粹,HTML集合一直與文檔保持著連接,每次你需要最新的信息時匆绣,都會重復執(zhí)行查詢的過程驻右,哪怕只是獲取集合里的元素個數(shù)(即訪問集合的length屬性)也是如此。這正是低效之源崎淳。
7. 遍歷 DOM
當你需要從多種方案中選擇時堪夭,最好為特定操作選擇最高效的API。
(1) 選擇器API
主要介紹 querySelectorAll()
var elements = document.querySelectorAll('#menu a');
elements包含一個引用列表拣凹,指向位于id = '#menu'的元素之中的所有 a 元素森爽。它會返回一個NodeList,包含著匹配節(jié)點的類數(shù)組對象嚣镜。
querySlector()用來獲取第一個匹配的節(jié)點爬迟。
相比于下面這段代碼:
var elements = document.getElementById('menu').getElementsById('a');
這段代碼中, elements返回的是一個HTML集合菊匿,而使用querySelectorAll將返回一個NodeList付呕,可以極大提高性能。
8. 重繪與重排
瀏覽器下載完頁面中所有組件之后跌捆,會解析并生成兩個內(nèi)部數(shù)據(jù)結構
DOM樹:表示頁面結構
渲染樹:表示DOM節(jié)點如何顯示
DOM樹中的每一個需要顯示的節(jié)點在渲染樹中至少存在一個對應的節(jié)點(隱藏的DOM元素在渲染樹中沒有對應的節(jié)點)徽职,一旦dom和渲染樹構建完成,瀏覽器就開始繪制頁面元素
當DOM的變化影響了元素的幾何屬性(高和寬)佩厚,瀏覽器會重新計算元素的幾何屬性姆钉,受其影響的的其他元素的幾何屬性也會重新計算。
瀏覽器會使渲染樹中受到影響的部分失效抄瓦,并重新構造渲染樹潮瓶。這個過程稱為“重排”。完成重排后闺鲸,瀏覽器會重新繪制受影響的部分到屏幕中筋讨,該過程稱為重繪。
改變顏色只會引起重繪摸恍,因為其沒有改變元素的幾何屬性。
重排和重繪有昂貴的代價,所以立镶,盡可能減少壁袄。
有些改變會觸發(fā)整個頁面的重排,例如媚媒,當滾動條出現(xiàn)時嗜逻。
(1) 渲染樹變化的排隊與刷新
由于每次重排都會產(chǎn)生計算消耗,大多數(shù)瀏覽器通過隊列化修改并批量執(zhí)行來優(yōu)化重排過程缭召。
(2) 最小化重繪和重排
書中給出的方案是:合并多次對dom和樣式的修改栈顷,然后一次性處理掉。
我覺得嵌巷,我們應該禁止去直接操作dom萄凤,如果要操作,請使用class的方法搪哪,增加class來實現(xiàn)對dom的修改靡努。
書中還推薦了createDocumentFragment()方法來進行文檔存儲,再一次性插入文檔中晓折,減少重排惑朦。
不過,我建議大家也停止使用這個方法漓概,就是因為createDocumentFragment()開銷太大漾月,它會產(chǎn)生很多我們根本用不到的屬性,也正是因為這個原因胃珍,Vue2才廢除了Vue1對這個方法的使用梁肿,轉而使用另一種更高效的方法來提高渲染性能的。