1. 批量增加Dom
儘量使用修改innerHTML的方式而不是用appendChild的方式; 因為使用innerHTML開銷更小,速度更快,同時也更加記憶體安全.
有一點需要注意的是,用innerHTML方式新增時,一定不要在迴圈中使用 innerHTML += 的方式新增,這樣反而會使速度減慢; 而是應該中間用array快取起來,迴圈結束後呼叫 xx.innerHTML = array.join(‘’);的方式,或者至少儲存到string中再插到innerHTML中.
針對使用者列表一塊採用這種方式優化後,載入速度提升一倍.
2. 單個增加Dom
這裡是指要將新節點載入到一個內容不斷變化的節點的情形,對於內容穩定的節點來說,隨便怎麼加都沒有問題. 但是對於有動態內容的節點來說,為其新增子節點儘量使用 dom append的方式.
這是因為,dom append不會影響到其他的節點;而如果修改innerHTML屬性的話,該父節點的所有子節點都會從dom樹中剝離,再根據新的innerHTML值來重繪子節點dom樹;所有註冊到原來子節點的事件也會失效.
綜上,如果在一個有動態內容的節點上 出現了 innerHTML += 的程式碼,就該考慮是否有問題了.
3. 建立Dom節點
用createElement方式建立一個dom節點,有一個很重要的細節: 在執行完createElement程式碼之後,應該馬上append到dom樹中; 否則,如果在將這個孤立節點載入到dom樹之前所做的賦值它的屬性和innerHTML的操作都會引發該dom片段記憶體無法回收的問題. 這個不起眼細節,一旦遇到大量dom增刪操作,就會引發記憶體的災難.
4. 刪除Dom節點
刪除dom節點之前,一定要刪除註冊在該節點上的事件,不管是用observe方式還是用attachEvent方式註冊的事件,否則將會產生無法回收的記憶體.
另,在removeChild和innerHTML=’’二者之間,儘量選擇後者. 因為在sIEve(記憶體洩露監測工具)中監測的結果是用removeChild無法有效地釋放dom節點.
5. 建立事件監聽
現有的js庫都採用observe方式來建立事件監聽,其實現上隔離了dom物件和事件處理函式之間的迴圈引用,所以應該儘量採用這種方式來建立事件監聽.
6. 監聽動態元素
Dom事件預設是向上冒泡的,發生在子節點中的事件,可以由父節點來處理. Event的 target/srcElement 仍是產生事件的最深層子節點. 這樣,對於內容動態增加並且子節點都需要相同的事件處理函式的情況,可以把事件註冊上提到父節點上,這樣就不需要為每個子節點註冊事件監聽了.
同時,這樣做也避免了產生無法回收的記憶體.即使是用Prototype的observe方式註冊事件並在刪除節點前呼叫stopObserving,也會產生出少量無法回收的記憶體,所以應該儘量少的為dom節點註冊事件監聽.
所以,當程式碼中出現在迴圈裡註冊事件時,也是我們該考慮事件上提機制的時候了.
7. HTML提純
HTML提純體現的是一種各負其責的思想. HTML只用來顯示,儘量不出現和顯示無關的屬性.比如onclick事件,比如自定義的物件屬性.
事件可以用前面的方法避免, 物件屬性指的是這樣的一種情景: 通常情況下,動態增加的內容都是有個物件和它對應,比如聊天室的使用者列表,每個顯示使用者的dom節點都有一個user物件和它對應,這樣在html中,應該僅保留一個id屬性和user物件對應,而其他的資訊,則應通過user物件去獲取.
基於PrototypeJS,寫了一個Dom生成器,以提供簡單高效的HTML操作介面的語法和語義優化:
作者:長沙
1. 使用類似JSONML的格式(HTML in JSON)描述DOM結構,以補充HTML轉義字串的表達形式,規範格式中可支援以下HTML語義:
l 標籤名
l 屬性(識別符號,類名,事件名,內聯樣式等)
注:事件名上註冊的偵聽函式將自動通過Event.observe方式新增
l 巢狀標籤
格式規範可簡單描述為:
{
tag:string, // 元素的標記名,如果沒有,預設為div
children|cn: string|Array|json, // 子結點對應的json陣列或位元組點的html或單個json
html:string, // 對應的html,如果有cn或children屬性就忽略
style:function|string|json, // 元素的樣式,可以是函式,字串,json物件
cls:string, // 元素的class屬性的值
x:y // x表示其他名字,y表示變數值、非空字串
onXXX: function // 以on為首的屬性是事件偵聽器
}
一個具體的例子為:
var list = DomBuilder.append('my-div', {
tag : 'ul',
cls : 'my-list',
children : [
{
tag : 'li',
id : 'item1',
html : 'List Item 1',
onclick : function() {
alert('List Item1 Clicked')
}
},
{
tag : 'li',
id : 'item2',
html : 'List Item 2',
customattr : 'customValue'
}]
});
2. 在實際修改DOM結構時,提供兩種方式可供呼叫者選擇,以便應需使用:
l W3C標準DOM操作(appendChild, removeChild)方式
l 使用innerHTML,insertAdjacentHTML等的實效模式
一個具體例子為:
DomBuilder.useDom = true; //顯示申明使用W3C Dom操作方式
3. 另外提供DOM節點的有效回收方法
var abandoned = DomBuilder.destroy(list); //將DOM節點從文件中移除