本文為大家介紹Dom節點進行最佳化的方案。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
DOM操作對效能影響最大是因為它導致了瀏覽器的重繪和回流,我們都知道頁面UI的變更都是透過DOM操作實現的,DOM雖然提供了許多api方便我們操作dom,但DOM操作的代價很高,頁面前端程式碼的效能瓶頸也大多集中在DOM操作上,所以前端效能最佳化的一個主要的重點就是DOM操作的最佳化。
瀏覽器渲染機制:
瀏覽器渲染頁面
瀏覽器解析HTML 文檔的來源碼,然後建構出一個DOM 樹,遇到樣式就非同步計算。
非同步計算好的樣式與dom樹合成,建構 render 樹。
進行佈局(layout) render 樹。
進行繪製(painting) render 樹。
DOM樹與render樹的差別在於:樣式為display:none;的節點會在DOM樹而不在渲染樹。瀏覽器繪製了之後便開始解析js文件,根據js確定是否重繪和重排。
頁面變更發生的動作:
js是單執行緒的,重繪和重排會阻塞使用者的操作以及影響網頁的效能
#1 、改變dom多個樣式,使用class,而非style,減少多次觸發回流重繪
範例:改變dom元素寬高
var dom = document.getElementById('box') dom.style.width = '300px' dom.style.height = '300px'//访问了三次dom,触发了两次回流和两次重绘
優化後:
.change { width: 300px; height: 300px; } document.getElementById('p').className = 'change'//只触发一次
2、清單類型批次修改,脫離文件流再恢復,利用樣式為display:none;的節點會在DOM樹中而不在渲染樹中不會造成重繪回流。
如果要在一個dom集合中,給每個dom子節點加一個class,我們可以遍歷給每個節點加上class,這樣就觸發了多次的重繪和回流
/* //需要加入的样式 .change { width: 300px; height: 300px; } */ var ul = document.getElementsByTagName('ul') var lis = document.getElementsByTagName('li') ul.style.display = 'none' for(var i = 0; i < lis.length; i++) { lis[i].className = 'change'; } ul.style.display = 'block'
3、DocumentFragment
虛擬DOM其實就是一個對象,js提供了reateDocumentFragment()方法用於建立一個空的虛擬節點對象,DocumentFragment節點不屬於文檔樹,當需要加入多個dom元素時,如果先將這些元素加入DocumentFragment中,然後再將DocumentFragment物件加入渲染樹上,會減少頁面渲染dom的次數,效率會明顯提升。
var frag = document.createDocumentFragment() //创建一个虚拟节点对象 for(var i = 0; i < 10; i++) { var li = document.createElement("li") li.innerHTML = '我是第' + i + 1 + '个元素' frag.appendChild(li) //将li元素加到虚拟节点对象上 } ul.appendChild(frag) //将虚拟节点对象加到ul上
其它
1、事件委託,利用瀏覽器事件,冒泡捕獲減少頁面事件綁定,我們可以指定一個事件處理程序就可以管理某一類型的所有事件。事件函數過多會佔用大量內存,而且綁定事件的DOM元素越多會增加訪問dom的次數,對頁面的交互就緒時間也會有延遲。
// 事件委托前 var lis = document.getElementsByTagName('li') for(var i = 0; i < lis.length; i++) { lis[i].onclick = function() { console.log(this.innerHTML) }} // 利用浏览器事件通过父元素委托事件给子元素 var ul = document.getElementsByTagName('ul')ul.onclick = function(event) { //也可以做判断给指定的子元素绑定事件 console.log(event.target.innerHTML)};
2、在循環中的優化減少操作dom次數
//例子1:减少在计算过程中操作dom // 优化前,访问了好多次dom,这些都是细节问题,有经验的绕过,小白平常多注意就行 for(var i = 0; i < 10; i++) { document.getElementById('el').innerHTML += '1'} // 优化后 var str = ''for(var i = 0; i < 10; i++) { str += '1'}document.getElementById('el').innerHTML = str/
#這樣看獲取你體驗不到快取節點長度的作用,請看下面的範例
//不缓存 var ps = document.getElementsByTagName("p"), i, p; for( i=0; i<ps.length; i++ ){ p = document.createElement("p"); document.body.appendChild("p"); }造成死循环,每次执行for循环都会动态获取ps的长度,而我们每次进入循环都增加了一个DOM(p),ps的长度也+1. //缓存 var ps = document.getElementsByTagName("p"), i, p,len; for( i=0;len=ps.length;i<len; i++ ){ p = document.createElement("p"); document.body.appendChild("p"); }//使用变量保存ps的长度。
3、選擇器差異
取得元素最常見的有兩種方法,getElementsByXXX()和queryselectorAll(),這兩種選擇器差異是很大的,前者是取得動態集合,後者是取得靜態集合
// 假设一开始有2个livar lis = document.getElementsByTagName('li') // 动态集合 var ul = document.getElementsByTagName('ul')[0] for(var i = 0; i < 3; i++) { console.log(lis.length) var newLi = document.createElement('li') ul.appendChild(newLi)}// 输出结果:2, 3, 4 // 优化后 var lis = document.querySelectorAll('li') // 静态集合 var ul = document.getElementsByTagName('ul')[0] for(var i = 0; i < 3; i++) { console.log(lis.length) var newLi = document.createElement('li') ul.appendChild(newLi)}// 输出结果:2, 2, 2
對靜態集合的操作不會造成對文件的重新查詢,相較於動態集合更加最佳化。
推薦學習:javascript影片教學
#以上是Dom節點如何進行優化的詳細內容。更多資訊請關注PHP中文網其他相關文章!