Douglas Crockford谈Ajax性能

Douglas Crockford谈Ajax性能

Douglas Crockford谈Ajax性能

2008年12月25日,星期四
这是Douglas Crockford在圣诞节前的一个讲座。我觉得对于前端工程师来说算是一个“新年大片”。英文好的建议直接去看原文 http://ericmiraglia.com/blog/?p=140感谢Eric Miraglia提供了完整的文字记录。
Crock先用< 记忆碎片 >这部电影作为引子。我还没看过,一定要找来看看。
如 果要向不太了解JSON的解释JSON为什么好Crock这句话说的很清楚“A JSON message is less work for the server to generate, it moves much faster on the wire because it’s smaller, it’s less work for the browser to parse and render than an HTML document.”
“The page is an application with a data connection to a server.”Crock对Ajax Revolution标题的注解。他提到“the client dose not need a copy of the database. it just needs, at any moment, just enough information to serve the user”,后端把所有数据都交给前端处理,后端是省事了,而浏览器端的性能则变的极差。“the client and the server are in a dialog, and we make the messages between them be as small as possible”,这是一个关键原则。just-in-time data delivery(按需传输数据)。
这 些道理其实尽人皆知,但在设计复杂的应用时,在进行多人协作的项目时,仍然会犯这样的错误。为什么?因为如果真的要实现一个客户端和服务器端的高效对话, 服务器端的设计就会变的复杂,我充分理解后端工程师也要考虑他们的问题。但为了更高效率,后端多花一些心思是值得的,因为目前浏览器还是一个非常低效的应 用平台(这也是Google推出Chrome的原因),存在显著的安全问题和性能问题。
Crock举的这个memoizer(缓存器)的例子非常经典:
var fibonacci = function(n){
return n < 2 ? n :
fibonacci(n - 1) + fibonacci(n - 2);
};
fibonacci(40);
执行自己 331,160,280 次
var memoizer = function(memo, fundamental){
var shell = function(n){
var result = memo[n];
if(typeof result !== ‘number’){
result = fundamental(shell, n);
memo[n] = result;
}
return result;
}
return shell;
}
var fibonacci = memoizer([0, 1], function(recur, n){
return recur(n - 1) + recur(n - 2);
});
优化后的程序仅执行了38次。”the key to optimization is work avoidance.” (优化的关键是“逃避工作”的技巧)

Crock提到两种优化思路:streamline和special casing

streamline(简 化为使效率更高):更换算法(选择更好的算法)、逃避工作、清除冗余代码(项目反复修改每次只是增加,往往忽略清除没用的代码)而且”These things are always good to do”这些事情应该一直去做而不是等到出现性能问题之后再解决。

special casing理解成特别包装更好一些。为了解决某些特别需求在原有代码基础上所做的包装,优其是对公共组件。会导致冗余代码越来越多,增加代码的 size,同时还会增加更多的测试路径(如果代码要进行白盒测试,无疑测试成本将增加很多)。要知道产品的需求永远是善变的。处理这种特殊需求可以写一些 “适配器”或“桥接器”,不用的话就直接拿掉。

 

“Avoid unnecessary displays or animation”很多产品经理认为花哨的交互就是好的用户体验,或者就是认为它够shiny,殊不知它带来的负面影响远比它的作用要大的多。 Crock是这么说的“A ‘wow’ effect is definitely worthwhile if it improves the user’s productivity, or improves the value of the experience to user.If it’s there just to show that we can do that, I think it’sa waste of our time, it’sa waste of the user’s time.”(译:如果这样做能提高用户的生产力或提升用户体验的价值,一个‘喔噻’的效果是价得的。但如果它的存在只是为了表明,我们能够做到这一点, 我认为这是浪费我们的时间,这是浪费用户的时间)
重 点解决影响最大的性能问题。看似费话,其实在开发中避重就轻的事没少干。关键首先要准确的判断出哪些环节是最大的症结所在。The bottleneck tends to be the DOM interface — DOM is a really inefficient API.(译: 瓶颈倾向于DOM接囗 - DOM是一个相当低效的API),还会有reflow计算的问题。“So touch the DOM lightly, if you can.”怎么才能“touch lightly”,Crock告诉我们两个技巧:一个是在创建的结点添加到DOM树上之前对其进行操作,这样不会有回流计算的问题。二是用 innerHTML,它只会跟DOM发生一次关系。
var d = document.createElement(”div”);
d.style.height = ‘100px’;
d.style.border = ‘1px solid red’;
d.style.color = ‘green’;
…(该办的事先办完)…
//最后再添加到dom树中
document.body.appendChild(d);

“Don’t Tune For Quirks”不要调那些浏览器的怪异问题。建议首先是尽量绕开这些quirks,虽然通过一些技巧可以解决这些问题,但也许在其它浏览器上性能就会变 差。而且当浏览器升级后修复了这个问题,之前所做的优化可能会变得更糟。Crock建议不做短期优化。
短短三十多分钟的讲座细细品味的话可以引出很多思考。
简单总结一下:
一是客户端要轻。程序不要臃肿,不要把所有数据都交给前端处理
二是代码是基础。寻求更好的算法,更好的设计模式
三是借住工具,不凭直觉
四是要抓重点。揪住主要问题解决
五是DOM操作是瓶颈
六是不要做短期优化

高性能的Ajax应用-Julien Lecomte

2007年12月21日,星期五

高性能的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/



java lover

你可能感兴趣的:(Douglas Crockford谈Ajax性能)