前端性能優化(DOM操做篇)

正巧看到在送書,因而乎找了找本身博客上記錄過的一些東西來及其無恥的蹭書了~~~javascript

小廣告:更多內容能夠看個人博客css

緩存DOM對象

JavaScript的DOM操做能夠說是JavaScript最重要的功能,咱們常常要根據用戶的操做來動態的增長和刪除元素,或是經過AJAX返回的數據動態生成元素。好比咱們得到了一個不少元素的數組data[],須要將其每一個值生成一個li元素插入到一個id爲container的ul元素中,最簡單(最慢)的方式是:java

var liNode, i, m;
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    document.getElementById("container").appendChild(liNode);
}

這裏每一次循環都會去查找id爲container的元素,效率天然很是低,因此咱們須要將元素在循環前查詢完畢,在循環中僅僅是引用就好了,修改代碼爲:數組

var ulNode = document.getElementById("container");
var liNode, i, m;
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    ulNode.appendChild(liNode);
}

緩存DOM對象的方式也常常被用在元素的查找中,查找元素應該是DOM操做中最頻繁的操做了,其效率優化也是大頭。在通常狀況下,咱們會根據須要,將一些頻繁被查找的元素緩存起來,在查找它或查找它的子孫元素時,以它爲起點進行查找,就能提升查找效率了。瀏覽器

在內存中操做元素

因爲DOM操做會致使瀏覽器的迴流,迴流須要花費大量的時間進行樣式計算和節點重繪與渲染,因此應當儘可能減小回流次數。一種可靠的方法就是加入元素時不要修改頁面上已經存在的元素,而是在內存中的節點進行大量的操做,最後再一併將修改運用到頁面上。DOM操做自己提供一個建立內存節點片斷的功能:document.createDocumentFragment(),咱們能夠將其運用於上述代碼中:緩存

var ulNode = document.getElementById("container");
var liNode, i, m;
var fragment = document.createDocumentFragment();
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    fragment.appendChild(liNode);
}
ulNode.appendChild(fragment);

這樣就只會觸發一次迴流,效率會獲得很大的提高。若是須要對一個元素進行復雜的操做(刪減、添加子節點),那麼咱們應當先將元素從頁面中移除,而後再對其進行操做,或者將其複製一個(cloneNode()),在內存中進行操做後再替換原來的節點app

一次性DOM節點生成

在這裏咱們每次都須要生成節點(document.createElement("li")),而後將其加入到內存片斷中,咱們能夠經過innerHTML屬性來一次性生成節點,具體的思路就是使用字符串拼接的方式,先生成相應的HTML字符串,最後一次性寫入到ul的innerHTML中。修改代碼爲:函數

var ulNode = document.getElementById("container");
var fragmentHtml = "", i, m;
for (i = 0, m = data.length; i < m; i++) {
    fragmentHtml += "<li>" + data[i] + "</li>";
}
ulNode.innerHTML = fragmentHtml;

這樣效率也會有提高,不過手動拼寫字符串是至關麻煩的一件事優化

經過類修改樣式

有時候咱們須要經過JavaScript給元素增長樣式,好比以下代碼:url

element.style.fontWeight = 'bold';
element.style.backgroundImage = 'url(back.gif)';
element.style.backgroundColor = 'white';
element.style.color = 'white';
//...

這樣效率很低,每次修改style屬性後都會觸發元素的重繪,若是修改了的屬性涉及大小和位置,將會致使迴流。因此咱們應當儘可能避免屢次爲一個元素設置style屬性,應當經過給其添加新的CSS類,來修改其CSS

.element {
    background-image: url(back.gif);
    background-color: #fff;
    color: #fff;
    font-weight: 'bold';
    /*...*/
}
element.className += " element";

經過事件代理批量操做事件

仍是以前那個ul和添加li,若是咱們須要給每一個li都綁定一個click事件,就可能寫出相似以下代碼:

var ulNode = document.getElementById("container");
var fragment = document.createDocumentFragment();
var liNode, i, m;
var liFnCb = function(evt){
    //do something
};
for (i = 0, m = data.length; i < m; i++) {
    liNode = document.createElement("li");
    liNode.innerText = data[i];
    liNode.addEventListener("click", liFnCb, false);
    fragment.appendChild(liNode);
}
ulNode.appendChild(fragment);

這裏每一個li元素都須要執行一次addEventListener()方法,若是li元素數量一多,就會下降效率。因此咱們能夠經過事件代理的方式,將事件綁定在ul上,而後經過event.target來肯定被點擊的元素是不是li元素,同時咱們也可使用innerHTML屬性一次性建立節點了,修改代碼爲:

var ulNode = document.getElementById("container");
var fragmentHtml = "", i, m;
var liFnCb = function(evt){
    //do something
};
for (i = 0, m = data.length; i < m; i++) {
    fragmentHtml += "<li>" + data[i] + "</li>";
}
ulNode.innerHTML = fragmentHtml;
ulNode.addEventListener("click", function(evt){
    if(evt.target.tagName.toLowerCase() === 'li') {
        liFnCb.call(evt.target, evt);
    }
}, false);

這樣事件綁定的代碼就只要執行一次,能夠監聽全部li元素的事件了。固然若是須要移除事件回調函數,咱們也不須要循環遍歷全部的li元素,只須要移除ul元素上的事件處理就好了

相關文章
相關標籤/搜索