|
insertAdjacentHTML的速度也給測出來看看是否合適大量數(shù)據(jù)時(shí)IE下,不用innerHTML的速度。
這里的主要測試不是指生成數(shù)據(jù)時(shí)的速度,指的是匹配速度 ,例如 我這里的匹配速度 我測的10000條數(shù)據(jù),有效數(shù)據(jù)為1000-1100條,輸出復(fù)雜的HTML,速度為360ms左右,方法為 正則匹配Match(有循環(huán)) 希望貼出您的測試數(shù)據(jù)。 行innerHTML和insertAdjacentHTML速度的測試,比均結(jié)果相差不會(huì)大于20ms(平均速度),在IE中insertAdjacentHTML速度還是很快的,在Mozilla下是得不償失的。 可以點(diǎn)擊這里進(jìn)行簡單的匹配測試 點(diǎn)擊這里進(jìn)行innerHTML和insertAdjacentHTML速度的測試,可以兼容Mozilla的寫這篇文章,其間我也是刪刪減減的,所以語句也不怎么通順,看的朋友也就辛苦了一些了。 本文主要是出于有朋友使用我原來寫的autocomplete的JS控件。當(dāng)數(shù)據(jù)量大的時(shí)候,會(huì)出現(xiàn)效率極其慢的情況,我在這段時(shí)間做出的一些測試也及一些經(jīng)驗(yàn),與大家分享,如果有錯(cuò)的地方,還請指出。 經(jīng)過測試,我們會(huì)發(fā)現(xiàn)如下的情況或者說的結(jié)論,如果您的測試結(jié)果與我的不符,請說明原因,以便相互學(xué)習(xí)。 1)當(dāng)一個(gè)較大的HTML字符串給到obj.innerHTML時(shí)會(huì)出現(xiàn)麻煩。也就是說當(dāng)一個(gè)較大的字符串在賦予一個(gè)Element的innerHTML時(shí),這個(gè)過程將可能是我們無法容忍的。(而事實(shí)上這并非JS的錯(cuò),而確實(shí)是String數(shù)據(jù)量太多) 2)用拼合字符串的方法可以使效率提高,在字符串較大時(shí),2)的情況仍然出現(xiàn)。超過一定的數(shù)量,速度會(huì)明顯慢下來。 3)正則匹配的方法會(huì)比平常遍歷的方法要高效一些。 4)在執(zhí)行過程中,綁定事件的時(shí)間會(huì)花費(fèi)更多一些。測試在1w條數(shù)據(jù)情況下,大約是匹配以及生成HTML數(shù)據(jù)的30倍,也就是說生成數(shù)據(jù)總花費(fèi)100ms,而綁定事件則需要3000ms。 5)總體來說。IE的速度要比Mozilla要慢(我用的是Firefox1.5做的測試)。 6)大數(shù)據(jù)量時(shí),不要用DOM生成Element。 7)非JS內(nèi)置方法,也許會(huì)引發(fā)很多時(shí)間過多重復(fù)的勞動(dòng)而且可能事得其反。建議盡可能利會(huì)內(nèi)置方法。 總結(jié)問題: 一、在把字符串給到innerHTML上。 二、循環(huán)綁定事件所花費(fèi)時(shí)間。 三、生成我們需要的DIV所花時(shí)間。 四、不同的瀏覽器問題。 下面對癥下藥: 問題一 我們可以做的沒有其它的,只有盡可能少的HTML字符串,比如最基本的一個(gè)DIV,可以這樣寫 <div style="height:20px; font:9pt Verdana;"></div>也可以這樣寫<div class="c1"></div>,第二種就比第一種速度明顯要快的。如果還不行的話,請看下面這個(gè)方法對你是否合適 在做程序的時(shí)候突然想起來51js上PK tree,一位版主所寫的一棵樹,1百萬的一個(gè)節(jié)點(diǎn),動(dòng)態(tài)載入。只需要不到1秒。毫無疑問,肯定是取巧了,因?yàn)橹灰簧鷗ree的html就是一個(gè)很大的數(shù)量。這個(gè)樹的特別的地方就是生成樹時(shí),并非把1百萬的節(jié)點(diǎn)都一次生成innerHTML,而是只生成在視角范圍內(nèi)的節(jié)點(diǎn),當(dāng)滾動(dòng)條向下滾的時(shí)候,才動(dòng)態(tài)的再生成樹節(jié)點(diǎn)。這個(gè)方法至少我覺得思想很開闊,很有價(jià)值。 我們所知道,mySQL數(shù)據(jù)庫里取數(shù)據(jù)可以這么取。SELECT * FROM table limit 0,100,意思是只取數(shù)據(jù)庫中的0-100條數(shù)據(jù)。說到這里可能有些朋友也想到了,在JS中,我們可以利用這個(gè)方法來取數(shù)據(jù),將一個(gè)數(shù)組看作是一個(gè)表。只是單純的數(shù)據(jù)表,非二維表。如圖 利用這一些,我們可以把數(shù)據(jù)有效的值先取出來。如圖: 想想看。假如我們?nèi)∫粋€(gè)數(shù)組,下標(biāo)為10000,設(shè)生成一個(gè)autocomplete的節(jié)點(diǎn)HTML長度20(已經(jīng)非常小了"<div class="out">item</div>)。 匹配數(shù)據(jù)已知:有3000條數(shù)據(jù) 輸節(jié)字節(jié)數(shù)為:3000(asc碼)也就是3000*20=60000字節(jié) 而用limit方法,輸出為:10*20=200字節(jié)。 很明顯的差距! 之后我們便可以分步求解,即當(dāng)滾動(dòng)條出現(xiàn),或者按下down(方向鍵)再動(dòng)態(tài)的生成innerHTML。 8-13更新:測了一下,用自己寫的limit的速度,和自帶的Array.slice的速度比了一下,速度差不多,而且有的時(shí)候還比slice的速度還要快一些。 Array.prototype.limit = function(l, h) { var _a = this; var ret = []; l = l<0?0:l; h = h>_a.length?_a.length:h; for (var i=0; i<_a.length; i++) { if (i>=l && i<=h) ret[ret.length] = _a[i]; if (i>h) break; }; return ret; } 有興趣的朋友也可以自己測一下,貼出數(shù)據(jù),看看哪個(gè)效率更好。 問題二、 為什么我們還要循環(huán)來綁定事件呢? 還是由于問題一。 假設(shè)這樣寫 1) <div id="container"> <div onclick="handlerClick()">never-online</div> </div> 還可以這樣寫 2) <div id="container"> <div>never-online</div> </div> document.getElementById("container").childNodes[0].onclick=function(){handlerClick()}; 這樣也可以省掉一些字符串,從而節(jié)省字符串資源。但又需要把container的子元素再遍歷,所以也會(huì)花費(fèi)時(shí)間,用第一種方法還是第二種?我建議還是用第一種,但最好把字符串減到最低,如: <div id="container"> <div onclick="_c()">never-online</div> </div> 大數(shù)據(jù)量情況下,還是越少字符越好,雖然代碼不怎么美觀。 問題三、 生成DIV時(shí)我們可以這樣生成 var div = document.createElement("DIV"); div.onclick=function(){}; //TODO 也可以這樣用字符串 var sHtml = "<div onclick=foo()>val</div>"; 當(dāng)數(shù)量小時(shí),第一種速度會(huì)比第二種快。但當(dāng)達(dá)到一個(gè)數(shù)量級(jí)時(shí),第二種要明顯比第一種快??傮w來說第二種較好。因?yàn)榈诙N還可以更靈活,比如利用join,還有正則匹配。 問題四、 這個(gè)問題也不容忽視的。每個(gè)browser有不同的特點(diǎn),速度執(zhí)行也有不同,我個(gè)人覺得,這點(diǎn)和JS上優(yōu)化效率上是一樣的。 盡可能的利用瀏覽器本身的內(nèi)置方法,這樣大多數(shù)情況下也可以把效率提高。 那么如何能夠把腳本的效率提高起來呢? 1)用match匹配,一個(gè)aCache數(shù)組。循環(huán)match.length,并給aCache,之后用join(""),再給到innerHTML(此方法仍然需要循環(huán),而且需額外的一個(gè)數(shù)組做臨時(shí)數(shù)據(jù)存儲(chǔ)) 2)無需循環(huán),但必須在生成數(shù)據(jù)時(shí)也額外生成指定字符串。(此方法也需要額外的空間做臨時(shí)數(shù)據(jù))如圖: 3)寧可多次判斷,也不重復(fù)進(jìn)行一次重新匹配。e.g: input控件中第一次取到的值為:1,第二次按下的值為12 如果進(jìn)行判斷的話,可以事件存儲(chǔ)一個(gè)值,也就是前一次按下的值。如上面的值1。第二次按下時(shí)沒有退格,即再在前面的值中加一個(gè)字符2,那么我們將在前面1中匹配出的數(shù)據(jù)中進(jìn)行匹配。這樣可以大大的減輕循環(huán)的次數(shù)。 4)利用問題一中所寫的limit,將數(shù)據(jù)動(dòng)態(tài)取出。這些能夠很好的解決HTML字符串過大的問題,但此方法控制不當(dāng)?shù)脑?,也?huì)事得其反。 5) 利用range技巧來加入HTMLStr,也就是說,當(dāng)一個(gè)HTML字符串太大時(shí),再用innerHTML+=anotherHTMLStr,這樣的方法,也是會(huì)讓速度太慢,在IE中,我們可以用obj.insertAdjacentHTML("beforeEnd", anotherHTMLStr)這樣的方法來插入HTML,這個(gè)方法經(jīng)過測試,比較穩(wěn)定。而在Mozilla中,則要利用range的技巧來達(dá)到此目的,如下: if (browser.isMozilla) {
后記:效率問題沒有一個(gè)完整的解決方法,只有實(shí)踐中根據(jù)需要而定。因此,上面的方法僅供您參考,如果你也有一些好的方法,可以在評論中寫下您的經(jīng)驗(yàn),以便交流。HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sHTML) { var df; var r = this.ownerDocument.createRange(); switch (String(sWhere).toLowerCase()) { case "beforebegin": r.setStartBefore(this); df = r.createContextualFragment(sHTML); this.parentNode.insertBefore(df, this); break; case "afterbegin": r.selectNodeContents(this); r.collapse(true); df = r.createContextualFragment(sHTML); this.insertBefore(df, this.firstChild); break; case "beforeend": r.selectNodeContents(this); r.collapse(false); df = r.createContextualFragment(sHTML); this.appendChild(df); break; case "afterend": r.setStartAfter(this); df = r.createContextualFragment(sHTML); this.parentNode.insertBefore(df, this.nextSibling); break; } }; } |
|
|