高性能的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事件触发前完成
-如果你需要控制你的初始化代码,可以直接写在里,并把它放在靠近的位置
-否则,使用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 {
> // P
rocess 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/