2009年6月26日 星期五

高性能的Ajax應用-Julien Lecomte

高性能的Ajax應用-Julien Lecomte


第1部分 高性能的開發

1.為高性能計劃和設計
-從每一天開始就要計劃高性能
-跟產品經理和設計師緊密的合作
-理解設計的基本原理
-設計和性能之前做解釋和權衡
-提供選擇和展示各種可能(原型)
-挑戰自己,實現有挑戰性的設計(不要只說「不」)
-為了簡化設計和交互,有時候需要妥協

2.高性能工程開發:一些基本規則
-少就是多。不要做任何不必要的事。直到變得絕對重要,否則不要做。
-打破規則。只能在迫不得以的情況下,才能妥協並打破最佳做法。
-在提升可以感覺到的性能上下功夫。

3.衡量性能
-使用用戶真實環境測試
-在開發中,profile你的代碼
-使用自動的profiling/性能測試
-保持功能執行的歷史數據
-在產品中考慮保留一些(少量的)profiling代碼

第2部分 高性能的頁面下載

1.Yahoo!的14條性能原則
一個頁面工作於3(有時是重疊的)個階段-下載,渲染,運行。14條原則大部分作用於第1個階段。

2.資源優化
-最小化CSS和Javascript文件。推薦使用YUI Compressor(http://developer.yahoo.com/yui/compressor)壓縮。遠離那些所謂的先進的壓縮方案,如Packer。
-合併CSS和Javascript文件。在發佈的時候合併(http://www.julienlecomte.net/blog/2007/09/16)或在運行的時候合併。
-優化圖片資源。如:PngCrush(http://pmt.sourceforge.net/pngcrush)、PngOptimizer(http://psydk.org/PngOptimizer.php)等。

3.減小非壓縮代碼的大小
-下載和解析HTML、CSS、Javascript代碼的成本是很高的。
-用簡練寫法和寫更少的代碼
-用好Javascript庫
-在考慮將大的Javascript文件拆成小的文件(bundle)時,解析和編譯腳本時要花費大量額外的時間
-按需下載代碼(HTML、CSS、Javascript),如,Lazy Loading
* 參見,http://ajaxpatterns.org/On-Demand_Javascript
* 使用 YUI Loader
* Dojo的package system
* JSAN的Import System

4.優化初始渲染(1):綜合技巧
-Consider rendering the first view on the server
-關閉你的HTML標籤提高解析速度。參見http://msdn2.microsoft.com/en-us/library/ms533020.aspx#Close_Your_Tags
-考慮儘早緩存。
-下載基本的資源,延遲或按需下載其他資源。使用YUI ImageLoader。

5.優化初始渲染(2):不要一直等onload
-大部分DOM操作可以在onload事件觸發前完成
-如果你需要控制你的初始化代碼,可以直接寫在<script>裡,並把它放在靠近</body>的位置
-否則,使用YUI事件組件中的onDOMReady方法
YAHOO.util.Event.onDOMReady(function () {
// Do something here...
// e.g., attach event handlers.
});

6.優化初始渲染(3):在頁面下載後,再下載腳本。
-一個好的網站應該在Javascript失效下功能也應該是完整的
-因此,你可以延遲下載腳本
-這樣做對下載其他資源(樣式表、圖片等)是有好處的
-這樣做使網站下載更快

7.優化初始渲染(4):有條件的預下載
-預下載潛在的資源(Javascript、CSS、圖片等)真的可以增強用戶體驗。
-可是,在什麼時候進行功妙的預下載是關鍵,否則,預下載會影響用戶體驗。
-參見http://www.sitepoint.com/article/web-site-optimization-steps/3
-參見http://search.yahoo.com


第3部分 高性能的Javascript

1.減少符號查尋(1):範圍鏈
-每次訪問變量時都會執行查尋
-變量從當前範圍向上執行查尋
-因此,無論何時都在相同範圍中聲明和使用變量
-完全不要使用with,它會阻止編譯器生成代碼時訪問本地變量的速度(因為首先要遍歷原型鏈,然後是範圍鏈等)
-緩存外部變量到本地變量。
不好的寫法:
var arr = ...;
var globalVar = 0;
(function () {
var i;
for (i = 0; i < arr.length; i++) {
globalVar++;
}
})();

好的寫法:
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.減少符號查尋(2):原型鏈
-訪問主對象上的成員的速度比訪問原型鏈上的成員的速度快25%
-原型鏈越長查尋越慢
function A () {}
A.prototype.prop1 = ...;

function B () {
this.prop2 = ...;
}

B.prototype = new A();
var b = new B();//(譯者:prop2為b的主對象成員,而prop1是原型鏈上的成員)

3.優化對象實例化
-如果你需要創建很多對象,可以考慮添加成員到原型中,而不添加到單個對象的構造器中。
-這樣會減少內存的消耗
-然而會拖慢查尋查尋對象成員的速度

4.不要使用eval
-傳字符串到eval中,需要編譯和解釋,相當的慢!
-完全不要傳一個字符串到setTimeout和setInterval中。可以使用匿名函數代替。
setTimeout(function () {
// Code to execute on a timeout
}, 50);
-完全不要eval做為方法的構造器。

5.優化字符串連接
-在IE(JScript)中,連接兩個字符串會導致一個新的字符串被重新分配資源,同時兩個原字符串被覆制:
var s = "xxx" + "yyy";
s += "zzz";
-因此在IE中,添加字符串到數組中然後再用Array.join連接比直接用+連接快很多(不要用在簡單的連接中)
-其他Javascript引擎(WebKit、SpiderMonkey)已經對字符串連接做了優化
-使用YUI Compressor!

6.優化正則表達式
-儘量不要用RegExp構造,除非你的正則表達式需要實時創建。
-使用test方法測試一個pattern(exec方法會有小的性能問題)
-使用非捕獲組(?:)
-保持pattern的簡單

7.緩存
-在下面情況下應用緩存是合理的:
* 更低成本的獲取一個值
* 值會被經常讀取
* 值不經常改變
-會增加內存消耗(權衡)
-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.如何控制長時間運行處理的Javascript
-在Javascrit的長時間運行處理過程中,整個瀏覽器會被凍結
-因此為了維持好的用戶體驗,確保Javascript一個線程在約300兆秒內完成
-你可以通過用setTimeout將長運行處理拆成的更小處理單元串起來執行
-更多見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 {
// Process next chunk
setTimeout(arguments.callee, 0);
}
})();
}

9.綜合技巧
-簡單的操作符往往比相應的方法要快
c = Math.min(a, b);
c = a < b ? a : b;//更快

myArray.push(value);
myArray[myArray.length] = value;//比上面快
myArray[idx++] = value;//比上面快

-避免使用try...catch在影響性能的部分:
不好的寫法:
var i;
for (i = 0; i < 100000; i++) {
try {
...
} catch (e) {
...
}
}
好的寫法:
var i;
try {
for (i = 0; i < 100000; i++) {
...
}
} catch (e) {
...
}
-If possible, avoid for...in in performance-critical sections
-無論何時分支條件都不改變的情況下,分支應該在外面,不要在裡面:
不好的寫法:
function fn () {
if (...) {
...
} else {
...
}
}

好的寫法:
var fn;
if (...) {
fn = function () {...};
} else {
fn = function () {...};
}


第4部分 高性能的動態HTML

1.文檔樹的修改:使用innerHTML
注意事項http://www.julienlecomte.net/blog/2007/12/38

2.文檔樹的修改:使用cloneNode
注意:expando屬性或附加的事件會丟失

3.文檔樹的修改:使用DocumentFragment
-DocumentFragment(DOM Level 1 Core)是一個輕量級的文檔對象
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);
-它僅僅支持常規DOM方法和屬性的子集
-IE實現DocumentFragment不服從W3C規範

4.限制事件柄的個數
-附加事件到上百個元素上的成本很高
-多個事件柄會增加潛在的內存漏洞
-解決方案:使用事件委託機制,一種依靠事件冒泡的機制

5.限制回流(Reflow)


第5部分 高性能的佈局和CSS

綜合技巧
-使用CSS Sprites
-避免使用Javascript佈局
-避免使用IE表達式
-避免使用IE濾鏡(或儘可能少用)
-優化Table佈局
-優化CSS選擇器http://developer.mozilla.org/en/docs/Writing_Efficient_CSS


第6部分 高性能的Ajax

1.綜合技巧
-完全不要使用同步的XMLHttpRequest。參見http://yuiblog.com/blog/2006/04/04/synchronous-v-asynchronous
-編程處理網絡超時
-解決方案:使用YUI Connection Manager
var callback = {
success: function () { /* Do something */ },
failure: function () { /* Do something */ },
timeout: 5000
};
YAHOO.util.Connect.asyncRequest("GET", url, callback);

2.提升可以感覺到的網絡延遲體驗
-如果數據在提交到服務器端之前經過本地校驗,通常請求的成功率達99.9%
-因此,為了優化用戶體驗,我們可以採用下面的Pattern:
* 當請求發出時要更新UI
* Lock the UI/data structures with the finest possible granularity.
* 讓用戶知道發生了什麼事
* 讓用戶知道為什麼UI被鎖定
* 當成功返回結果後要及時解除鎖定
* 要用優雅的方式處理錯誤

3.綜合技巧
-知道並發HTTP/1.1連接的最大數量
-如果後端支持,支持多元的Ajax請求
-Piggyback unsollicited notifications in a response to an Ajax request.
-用JSON代替XML做為數據交換格式
-推送,不要輪詢。使用COMET向瀏覽器發送實時的通知
-考慮使用本地存儲器緩存數據。
* IE的userData
* Flash本地存儲
* DOM:Storage(WHATWG持久存儲API, 已在Firefox2中實現)
* 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/)

網誌存檔