Douglas Crockford談Ajax性能
2008年12月25日,星期四
streamline(簡(jiǎn) 化為使效率更高):更換算法(選擇更好的算法)、逃避工作、清除冗余代碼(項(xiàng)目反復(fù)修改每次只是增加,往往忽略清除沒(méi)用的代碼)而且”These things are always good to do”這些事情應(yīng)該一直去做而不是等到出現(xiàn)性能問(wèn)題之后再解決。
高性能的Ajax應(yīng)用-Julien Lecomte
2007年12月21日,星期五高性能的Ajax應(yīng)用-Julien Lecomte
第1部分 高性能的開(kāi)發(fā)
1.為高性能計(jì)劃和設(shè)計(jì)
-從每一天開(kāi)始就要計(jì)劃高性能
-跟產(chǎn)品經(jīng)理和設(shè)計(jì)師緊密的合作
-理解設(shè)計(jì)的基本原理
-設(shè)計(jì)和性能之前做解釋和權(quán)衡
-提供選擇和展示各種可能(原型)
-挑戰(zhàn)自己,實(shí)現(xiàn)有挑戰(zhàn)性的設(shè)計(jì)(不要只說(shuō)“不”)
-為了簡(jiǎn)化設(shè)計(jì)和交互,有時(shí)候需要妥協(xié)
2.高性能工程開(kāi)發(fā):一些基本規(guī)則
-少就是多。不要做任何不必要的事。直到變得絕對(duì)重要,否則不要做。
-打破規(guī)則。只能在迫不得以的情況下,才能妥協(xié)并打破最佳做法。
-在提升可以感覺(jué)到的性能上下功夫。
3.衡量性能
-使用用戶真實(shí)環(huán)境測(cè)試
-在開(kāi)發(fā)中,profile你的代碼
-使用自動(dòng)的profiling/性能測(cè)試
-保持功能執(zhí)行的歷史數(shù)據(jù)
-在產(chǎn)品中考慮保留一些(少量的)profiling代碼
第2部分 高性能的頁(yè)面下載
1.Yahoo!的14條性能原則
一個(gè)頁(yè)面工作于3(有時(shí)是重疊的)個(gè)階段-下載,渲染,運(yùn)行。14條原則大部分作用于第1個(gè)階段。
2.資源優(yōu)化
-最小化CSS和Javascript文件。推薦使用YUI Compressor(http://developer.yahoo.com/yui/compressor)壓縮。遠(yuǎn)離那些所謂的先進(jìn)的壓縮方案,如Packer。
-合并CSS和Javascript文件。在發(fā)布的時(shí)候合并(http://www.julienlecomte.net/blog/2007/09/16)或在運(yùn)行的時(shí)候合并。
-優(yōu)化圖片資源。如:PngCrush(http://pmt.sourceforge.net/pngcrush)、PngOptimizer(http://psydk.org/PngOptimizer.php)等。
3.減小非壓縮代碼的大小
-下載和解析HTML、CSS、Javascript代碼的成本是很高的。
-用簡(jiǎn)練寫(xiě)法和寫(xiě)更少的代碼
-用好Javascript庫(kù)
-在考慮將大的Javascript文件拆成小的文件(bundle)時(shí),解析和編譯腳本時(shí)要花費(fèi)大量額外的時(shí)間
-按需下載代碼(HTML、CSS、Javascript),如,Lazy Loading
* 參見(jiàn),http://ajaxpatterns.org/On-Demand_Javascript
* 使用 YUI Loader
* Dojo的package system
* JSAN的Import System
4.優(yōu)化初始渲染(1):綜合技巧
-Consider rendering the first view on the server
-關(guān)閉你的HTML標(biāo)簽提高解析速度。參見(jiàn)http://msdn2.microsoft.com/en-us/library/ms533020.aspx#Close_Your_Tags
-考慮盡早緩存。
-下載基本的資源,延遲或按需下載其他資源。使用YUI ImageLoader。
5.優(yōu)化初始渲染(2):不要一直等onload
-大部分DOM操作可以在onload事件觸發(fā)前完成
-如果你需要控制你的初始化代碼,可以直接寫(xiě)在里,并把它放在靠近的位置
-否則,使用YUI事件組件中的onDOMReady方法
YAHOO.util.Event.onDOMReady(function () {
// Do something here…
// e.g., attach event handlers.
});
6.優(yōu)化初始渲染(3):在頁(yè)面下載后,再下載腳本。
-一個(gè)好的網(wǎng)站應(yīng)該在Javascript失效下功能也應(yīng)該是完整的
-因此,你可以延遲下載腳本
-這樣做對(duì)下載其他資源(樣式表、圖片等)是有好處的
-這樣做使網(wǎng)站下載更快
7.優(yōu)化初始渲染(4):有條件的預(yù)下載
-預(yù)下載潛在的資源(Javascript、CSS、圖片等)真的可以增強(qiáng)用戶體驗(yàn)。
-可是,在什么時(shí)候進(jìn)行功妙的預(yù)下載是關(guān)鍵,否則,預(yù)下載會(huì)影響用戶體驗(yàn)。
-參見(jiàn)http://www.sitepoint.com/article/web-site-optimization-steps/3
-參見(jiàn)http://search.yahoo.com
第3部分 高性能的Javascript
1.減少符號(hào)查尋(1):范圍鏈
-每次訪問(wèn)變量時(shí)都會(huì)執(zhí)行查尋
-變量從當(dāng)前范圍向上執(zhí)行查尋
-因此,無(wú)論何時(shí)都在相同范圍中聲明和使用變量
-完全不要使用with,它會(huì)阻止編譯器生成代碼時(shí)訪問(wèn)本地變量的速度(因?yàn)槭紫纫闅v原型鏈,然后是范圍鏈等)
-緩存外部變量到本地變量。
不好的寫(xiě)法:
var arr = …;
var globalVar = 0;
(function () {
var i;
for (i = 0; i < arr.length; i++) {
globalVar++;
}
})();
好的寫(xiě)法:
var arr = …;
var globalVar = 0;
(function () {
var i, l, localVar;
l = arr.length;
localVar = globalVar;
for (i = 0; i < l; i++) {
localVar++;
}
globalVar = localVar;
})();
2.減少符號(hào)查尋(2):原型鏈
-訪問(wèn)主對(duì)象上的成員的速度比訪問(wèn)原型鏈上的成員的速度快25%
-原型鏈越長(zhǎng)查尋越慢
function A () {}
A.prototype.prop1 = …;
function B () {
this.prop2 = …;
}
B.prototype = new A();
var b = new B();//(譯者:prop2為b的主對(duì)象成員,而prop1是原型鏈上的成員)
3.優(yōu)化對(duì)象實(shí)例化
-如果你需要?jiǎng)?chuàng)建很多對(duì)象,可以考慮添加成員到原型中,而不添加到單個(gè)對(duì)象的構(gòu)造器中。
-這樣會(huì)減少內(nèi)存的消耗
-然而會(huì)拖慢查尋查尋對(duì)象成員的速度
4.不要使用eval
-傳字符串到eval中,需要編譯和解釋,相當(dāng)?shù)穆?br />
-完全不要傳一個(gè)字符串到setTimeout和setInterval中。可以使用匿名函數(shù)代替。
setTimeout(function () {
// Code to execute on a timeout
}, 50);
-完全不要eval做為方法的構(gòu)造器。
5.優(yōu)化字符串連接
-在IE(JScript)中,連接兩個(gè)字符串會(huì)導(dǎo)致一個(gè)新的字符串被重新分配資源,同時(shí)兩個(gè)原字符串被復(fù)制:
var s = “xxx” + “yyy”;
s += “zzz”;
-因此在IE中,添加字符串到數(shù)組中然后再用Array.join連接比直接用+連接快很多(不要用在簡(jiǎn)單的連接中)
-其他Javascript引擎(WebKit、SpiderMonkey)已經(jīng)對(duì)字符串連接做了優(yōu)化
-使用YUI Compressor!
6.優(yōu)化正則表達(dá)式
-盡量不要用RegExp構(gòu)造,除非你的正則表達(dá)式需要實(shí)時(shí)創(chuàng)建。
-使用test方法測(cè)試一個(gè)pattern(exec方法會(huì)有小的性能問(wèn)題)
-使用非捕獲組(?:)
-保持pattern的簡(jiǎn)單
7.緩存
-在下面情況下應(yīng)用緩存是合理的:
* 更低成本的獲取一個(gè)值
* 值會(huì)被經(jīng)常讀取
* 值不經(jīng)常改變
-會(huì)增加內(nèi)存消耗(權(quán)衡)
-Memoization:
Module Pattern:
var fn = (function () {
var b = false, v;
return function () {
if (!b) {
v = …;
b = true;
}
return v;
};
})();
Store value in function object:
function fn () {
if (!fn.b) {
fn.v = …;
fn.b = true;
}
return fn.v;
}
Lazy function definition:
var fn = function () {
var v = …;
return (fn = function () {
return v;
})();
};
8.如何控制長(zhǎng)時(shí)間運(yùn)行處理的Javascript
-在Javascrit的長(zhǎng)時(shí)間運(yùn)行處理過(guò)程中,整個(gè)瀏覽器會(huì)被凍結(jié)
-因此為了維持好的用戶體驗(yàn),確保Javascript一個(gè)線程在約300兆秒內(nèi)完成
-你可以通過(guò)用setTimeout將長(zhǎng)運(yùn)行處理拆成的更小處理單元串起來(lái)執(zhí)行
-更多見(jiàn)http://www.julienlecomte.net/blog/2007/10/28/
-例子http://www.julienlecomte.net/blogfiles/javascript/long-running-js-process.html
function doSomething (callbackFn) {
// Initialize a few things here…
(function () {
// Do a little bit of work here…
if (termination condition) {
// We are done
callbackFn();
} else {
> // P
rocess next chunk
setTimeout(arguments.callee, 0);
}
})();
}
9.綜合技巧
-簡(jiǎn)單的操作符往往比相應(yīng)的方法要快
c = Math.min(a, b);
c = a < b ? a : b;//更快
myArray.push(value);
myArray[myArray.length] = value;//比上面快
myArray[idx++] = value;//比上面快
-避免使用try…catch在影響性能的部分:
不好的寫(xiě)法:
var i;
for (i = 0; i < 100000; i++) {
try {
…
} catch (e) {
…
}
}
好的寫(xiě)法:
var i;
try {
for (i = 0; i < 100000; i++) {
…
}
} catch (e) {
…
}
-If possible, avoid for…in in performance-critical sections
-無(wú)論何時(shí)分支條件都不改變的情況下,分支應(yīng)該在外面,不要在里面:
不好的寫(xiě)法:
function fn () {
if (…) {
…
} else {
…
}
}
好的寫(xiě)法:
var fn;
if (…) {
fn = function () {…};
} else {
fn = function () {…};
}
第4部分 高性能的動(dòng)態(tài)HTML
1.文檔樹(shù)的修改:使用innerHTML
注意事項(xiàng)http://www.julienlecomte.net/blog/2007/12/38
2.文檔樹(shù)的修改:使用cloneNode
注意:expando屬性或附加的事件會(huì)丟失
3.文檔樹(shù)的修改:使用DocumentFragment
-DocumentFragment(DOM Level 1 Core)是一個(gè)輕量級(jí)的文檔對(duì)象
var i, j, el, table, tbody, row, cell, docFragment;
docFragment = document.createDocumentFragment();
el = document.createElement(”div”);
docFragment.appendChild(el);
table = document.createElement(”table”);
el.appendChild(table);
tbody = document.createElement(”tbody”);
table.appendChild(tbody);
for (i = 0; i < 1000; i++) {
…
}
document.body.appendChild(docFragment);
-它僅僅支持常規(guī)DOM方法和屬性的子集
-IE實(shí)現(xiàn)DocumentFragment不服從W3C規(guī)范
4.限制事件柄的個(gè)數(shù)
-附加事件到上百個(gè)元素上的成本很高
-多個(gè)事件柄會(huì)增加潛在的內(nèi)存漏洞
-解決方案:使用事件委托機(jī)制,一種依靠事件冒泡的機(jī)制
5.限制回流(Reflow)
第5部分 高性能的布局和CSS
綜合技巧
-使用CSS Sprites
-避免使用Javascript布局
-避免使用IE表達(dá)式
-避免使用IE濾鏡(或盡可能少用)
-優(yōu)化Table布局
-優(yōu)化CSS選擇器http://developer.mozilla.org/en/docs/Writing_Efficient_CSS
第6部分 高性能的Ajax
1.綜合技巧
-完全不要使用同步的XMLHttpRequest。參見(jiàn)http://yuiblog.com/blog/2006/04/04/synchronous-v-asynchronous
-編程處理網(wǎng)絡(luò)超時(shí)
-解決方案:使用YUI Connection Manager
var callback = {
success: function () { /* Do something */ },
failure: function () { /* Do something */ },
timeout: 5000
};
YAHOO.util.Connect.asyncRequest(”GET”, url, callback);
2.提升可以感覺(jué)到的網(wǎng)絡(luò)延遲體驗(yàn)
-如果數(shù)據(jù)在提交到服務(wù)器端之前經(jīng)過(guò)本地校驗(yàn),通常請(qǐng)求的成功率達(dá)99.9%
-因此,為了優(yōu)化用戶體驗(yàn),我們可以采用下面的Pattern:
* 當(dāng)請(qǐng)求發(fā)出時(shí)要更新UI
* Lock the UI/data structures with the finest possible granularity.
* 讓用戶知道發(fā)生了什么事
* 讓用戶知道為什么UI被鎖定
* 當(dāng)成功返回結(jié)果后要及時(shí)解除鎖定
* 要用優(yōu)雅的方式處理錯(cuò)誤
3.綜合技巧
-知道并發(fā)HTTP/1.1連接的最大數(shù)量
-如果后端支持,支持多元的Ajax請(qǐng)求
-Piggyback unsollicited notifications in a response to an Ajax request.
-用JSON代替XML做為數(shù)據(jù)交換格式
-推送,不要輪詢。使用COMET向?yàn)g覽器發(fā)送實(shí)時(shí)的通知
-考慮使用本地存儲(chǔ)器緩存數(shù)據(jù)。
* IE的userData
* Flash本地存儲(chǔ)
* DOM:Storage(WHATWG持久存儲(chǔ)API, 已在Firefox2中實(shí)現(xiàn))
* Google Gears
* 其它
第7部分 性能工具
-YSlow? http://developer.yahoo.com/yslow
-Task Manager
-IE Leak Detector a.k.a Drip [ http://www.outofhanwell.com/ieleak/ ]
-Stopwatch profiling
* AjaxView [ http://research.microsoft.com/projects/ajaxview/ ]
* JsLex [ http://rockstarapps.com/pmwiki/pmwiki.php?n=JsLex.JsLex ]
* YUI profiler [ http://developer.yahoo.com/yui/profiler/ ]
-Venkman or Firebug Profiler [ http://www.getfirebug.com/ ]
(原文:http://yuiblog.com/blog/2007/12/20/video-lecomte/