高性能javascript

Post on 20-Dec-2014

1.148 Views

Category:

Education

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

High � Performance � JavaScript2011-09-09

11年9月13日星期二

About � Me

谢传贵(阿贵)

前端开发部-前端架构组

新浪微薄@十月光风

11年9月13日星期二

11年9月13日星期二

先来看一场微电影,时长6.5s

11年9月13日星期二

0s

11年9月13日星期二

0.5s

11年9月13日星期二

1.0s

11年9月13日星期二

1.5s

11年9月13日星期二

2.0s

11年9月13日星期二

2.5s

11年9月13日星期二

3.0s

11年9月13日星期二

3.5s

11年9月13日星期二

4.0s

11年9月13日星期二

4.5s

11年9月13日星期二

5.0s

11年9月13日星期二

5.5s

11年9月13日星期二

6.0s

11年9月13日星期二

6.5s

11年9月13日星期二

0.0 0.5 1.0 1.5

2.0 2.5 3.0 3.5

4.0 4.5

6.5

5.55.0

6.0

回顾

11年9月13日星期二

知己知彼,百战不殆

11年9月13日星期二

Topic

JavaScript运行过程

快速响应UI

更多编程实践

效率相关的工具

11年9月13日星期二

JavaScript运行的过程是怎么样的?

11年9月13日星期二

解释(interpreted)

编译(compiled)

从源码到可执行代码

11年9月13日星期二

(function(){alert(‘hello � world’)

})

0100100101010101

Runtime.exec(‘hello.exe’)

source.js(源码)

helloworld.exe(二进制码)

运行时环境

编译

编译

执行

11年9月13日星期二

(function(){alert(‘hello � world’)

})

Runtime.exec(‘hello.exe’)

source.js(源码)

helloworld.exe(二进制码)

运行时环境

解释

编译&运行

11年9月13日星期二

(function(){alert(‘hello � world’)

})

Runtime.exec(‘中间机器

解释

编译&运行

运行时环境(浏览器)解释器执行伪代码

11年9月13日星期二

(function(){alert(‘hello � world’)

})

Runtime.exec(‘中间机器

解释

编译&运行

运行时环境(浏览器)

JavaScript � Engine

11年9月13日星期二

JavaScript � 是解释型语言

11年9月13日星期二

JavaScript代码执行的过程

词法分析

语法分析

预编译

运行

语法检查

运行时

11年9月13日星期二

JavaScript代码执行的过程

词法分析

语法分析

预编译

运行

语法检查

运行时

将上下文中var申明 � 的变量放入”栈”中 � 并赋值为undefined

读入”定义”的函数

11年9月13日星期二

看个例子

11年9月13日星期二

(function(){alert(a); � // � 会报错么?if(false){

var � a=1; � }

})();

11年9月13日星期二

函数执行前,函数内部变量均被声明(function(){

alert(a); � // � 显示undefined,不报错if(false){

var � a=1; � //不会执行到,亦被声明}

})();

11年9月13日星期二

再来看个例子

11年9月13日星期二

test();function � test(){ �  alert(1);}test();function � test(){ �  alert(2);}test();var � test � = � function(){ �  alert(3);}test();test � = � function(){ �  alert(4);}test();

11年9月13日星期二

test();function � test(){//预编译定义,运行时略过 �  alert(1);}test();function � test(){//预编译定义,运行时略过 �  alert(2);}test();var � test � = � function(){//预编译声明,运行时赋值 �  alert(3);}test();test � = � function(){//运行时,变量赋值 �  alert(4);}test();

11年9月13日星期二

JavaScript性能会影响到用户体验吗?

11年9月13日星期二

JavaScript � 性能已经直接影响到用户体验

11年9月13日星期二

JavaScript � is � Slow!

11年9月13日星期二

11年9月13日星期二

11年9月13日星期二

JavaScript � is � Fast!

11年9月13日星期二

JavaScript引擎

V8 � C++

JaegerMonkey � C++

JScript � JSctipt.Net

Nitro � C++

Karakan � C++

...

11年9月13日星期二

http://ie.microsoft.com/testdrive/benchmarks/sunspider/default.html

JavaScript运行越来越快

11年9月13日星期二

IE8 IE6 Sougou IE7 IE9Tt Chrome Maxyhon Firefox TheworldSe360 Other Opera Safari

http://www.1688.com/11年9月13日星期二

IE840.60%

IE639.81%

Sougou5.03%

3.74%3.09%

1.67%1.24%1.17%1.11%0.99%0.96%0.37%0.13%0.10%

IE8 IE6 Sougou IE7 IE9Tt Chrome Maxyhon Firefox TheworldSe360 Other Opera Safari

http://www.1688.com/11年9月13日星期二

但如果不了解,不注意,依然有可能写出非常低效(Inefficient)的JavaScript代码

11年9月13日星期二

Douglas � Crockford Steve � Sounders Nicholas.C.Zakes

大牛11年9月13日星期二

UI线程

11年9月13日星期二

Input output<!DOCTYPE � html>

<html><head>

<title>1688首页</title></head><body>

<div>hello</div><script>

doc.write(‘helloworld’);</script><div>world</div>

</body></html>

浏览器是如何渲染的?

?

11年9月13日星期二

Fetch Parse Flow Paint

URL Cache Tree DisplayList

Pixels

浏览器渲染过程

http://technotes-himanshu.blogspot.com/2010/05/html-dom.html11年9月13日星期二

Flow

Event

Paint

Script

执行脚本过程中的渲染

11年9月13日星期二

JS执行和浏览器渲染

11年9月13日星期二

<!DOCTYPE html><html>

<head><title>1688首页</title>

</head><body>

<div id=”a”>hello</div><script>

doc.write(‘helloworld’);</script><div id=”b”>world</div></body>

</html>

构建DOM 树

document

head

title

body

div

script

div

textNodeJS引擎创建了

textNode

11年9月13日星期二

阻塞:JS运行会中断HTML的渲染

11年9月13日星期二

浏览器是单线程作业同一时刻只能做一件事情,要么是用户界面更新,要么是JavaScript脚本执行

11年9月13日星期二

在UI线程忙碌的情况下,UI更新和JS执行都会被加入UI队列里,直到当前任务执行完毕,才会被激活

11年9月13日星期二

UI � Rendering � Threadtime

DOM

1.构建DOM

2.更新 � UI

11年9月13日星期二

UI � Rendering � Threadtime

DOM

渲染出此时的DOM

UI � Update

11年9月13日星期二

UI � Rendering � Threadtime

DOM

UI � Update Exec � JS

3.JS � 脚本新增DOM节点

11年9月13日星期二

UI � RenderingThreadtime

DOM

UI � Update Exec � JS UI � Update

4.更新UI

11年9月13日星期二

JavaScript执行时间 � = � UI不可响应时间

11年9月13日星期二

UI � Update Exec � JS UI � Update

time

UI可响应的

11年9月13日星期二

UI � Update Exec � JS UI � Update

time

UI不可响应的

假死

11年9月13日星期二

JavaScript执行过程耗时越久,浏览器等待响应用户输入的时间就越长,

就有多不好的用户体验

11年9月13日星期二

浏览器对于JavaScript运行的限制

1.调用栈大小限制

2.长时间运行限制

主要分为两类:

11年9月13日星期二

��� ���, ��� �

11年9月13日星期二

-� ���

11年9月13日星期二

浏览器限制

IE:500万条语句

Firefox:10秒

Safari:5秒

Chrome:没有单独的长运行脚本限制,替代做法是依赖于其崩溃检测系统来处理此类问题

Opera:没有长运行脚本限制

11年9月13日星期二

多久才算太久?

11年9月13日星期二

如果JavaScript运行了整整几秒钟,那么很可能你做错了什么....

--Brendan � Eich

11年9月13日星期二

在100毫秒以内响应用户输入,用户会认为自己在直接操作界面中的对象.超过100毫秒意味着用户会感到自己与界面失去联系.

-- � Jokob � Nielsen

11年9月13日星期二

佳实践:少于50毫秒

11年9月13日星期二

JavaScript Load最佳实践

11年9月13日星期二

浏览器在进行JavaScript下载,解析和运行都是会阻塞页面渲染的?

11年9月13日星期二

<!DOCTYPE html><html>

<head><title>1688首页</title>

</head><body>

<div>hello</div><script src=”jQuery.js”></script><div>world</div>

</body></html>

11年9月13日星期二

UI � Update JavaScript UI � Update

结果

11年9月13日星期二

UI � Update jQuery.js UI � Update

结果

11年9月13日星期二

UI � Update UI � Updatedownload parse run

结果

11年9月13日星期二

<!DOCTYPE html><html>

<head><title>1688首页</title>

</head><body>

<div>hello</div><script src=”jQuery.js”></script><div>world</div><script src=”yui2.js”></script><div>1688</div><script src=”yui3.js”></script>

</body></html>

11年9月13日星期二

UI � Update JavaScript UI � Update

结果

JavaScript UI � Update JavaScript

11年9月13日星期二

JavaScript Load最佳实践1:把JS放到页面底部

11年9月13日星期二

11年9月13日星期二

UI � Update JavaScriptUI � Update

结果

JavaScriptUI � Update JavaScript

11年9月13日星期二

<!DOCTYPE html><html>

<head><title>1688首页</title>

</head><body>

<div>hello</div><div>world</div><div>1688</div><script src=”jQuery.js”></script><script src=”yui2.js”></script><script src=”yui3.js”></script>

</body></html>

11年9月13日星期二

JavaScript Load最佳实践2:JS合并

11年9月13日星期二

<!DOCTYPE html><html>

<head><title>1688首页</title>

</head><body>

<div>hello</div><div>world</div><div>1688</div><script src=”jQuery-yui2-yui3.js”></script>

</body></html>

11年9月13日星期二

UI � Update UI � Update

结果

JavaScriptUI � Update

11年9月13日星期二

当然我们可以通过工具来完成自动合并

11年9月13日星期二

独角兽

Google � Page � Speed � Module

...

11年9月13日星期二

JavaScript Load最佳实践3:无阻塞异步加载JS

11年9月13日星期二

可选方案

Script DOM Element

Script Defer

Script Async

Iframed JS

XML HttpRequest Script Injection

11年9月13日星期二

Script DOM Element原理var script = document.createElement("script"),body = document.body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild);

11年9月13日星期二

UI � Update UI � Updaterun

结果

download parse

Time

11年9月13日星期二

类库选择YUI2(YAHOO.util.Get.script)

jQuery( jQuery.ajax)

LABjs

headjs

requirejs

SeaJs

...

11年9月13日星期二

Script � Defer

<script � defer � src="foo.js"></script>

11年9月13日星期二

浏览器支持情况

7.0 3.5 4.0 ? 5.0

11年9月13日星期二

Script � Async

<script � async � src="foo.js"></script>

11年9月13日星期二

浏览器支持情况

7.0 3.5 ? ? 5.0

11年9月13日星期二

Iframed � JSfunction � iframedJS(s){ � 

document.write("<iframe � id= � 'i'></iframe>"); � var � d � = � document.getElementById("i").contentWindow.document; � d.write('<!doctype � html><html><body><scr' � + � 'ipt � src="'+s

+'"></scr' � + � 'ipt></body></html>'); � window.setTimeout((function(){d.close();}),0);}

11年9月13日星期二

对同一个iframe多次进行doc.open+write+close,会增加浏览历史记录

Firefox � doc.write � iframe至页面,可能不能马上取到其引用

domain的潜在问题

缺点

11年9月13日星期二

XML � HttpRequest � Script � Injectionvar � xhr � = � new � XMLHttpRequest();xhr.open(‘get’,‘file.js’,true);xhr.onreadystatechange=function(){

if(xhr.staus==4){if(xhr.status>=200&& � xhr.status<300|| � xhr.status==304){

var � script � = � document.createElemet(‘script’);script.type= � ‘text/javascript’;script.text � = � xhr.responseText;document.body.appendChild(script);

}}

}

11年9月13日星期二

特点

可先下载,但不立即执行主流浏览器都支持有同域的要求

11年9月13日星期二

JavaScript � Compression �  佳实践

11年9月13日星期二

JavaScript � Compression �  佳实践1.上线前压缩

11年9月13日星期二

可选工具

YUI � Compressor

Google �  � Closure � Compiler

UglifyJS

Packer

...

11年9月13日星期二

JavaScript � Compression �  佳实践2.网络传输压缩

11年9月13日星期二

可选方案

gzip

compress

deflate

identity

11年9月13日星期二

JavaScript � Exec �  佳实践

11年9月13日星期二

JavaScript � Exec �  佳实践 � 1:使用定时器让出时间片

11年9月13日星期二

UI � Thread

UI � Update-Button JavaScript-click JavaScript-click

0 50

UI � Update-Button

JavaScript-click

JavaScript-clickTimer � coder

UI � Queue

setTimeout() called Timer code queued

11年9月13日星期二

使用定时器处理数组var � todo � = � items.concat();

setTime(function(){//取得数组的下个元素进行处理process(todo.shift());

if(todo.length � > � 0){setTimeout(arguments.callee,25);

} � else � {callback(items);

}},25);

11年9月13日星期二

分割任务function � multistep(steps,args,callback){

var � tasks � = � steps.concat();setTimeout(function(){

var � task � = � tasks.shitf();task.apply(null,args � || � []);

if(tasks.length � > � 0){setTimeout(arguments.callee,25);

}else{callback();

}},25);

}11年9月13日星期二

JavaScript � Exec �  佳实践 � 2:Web � Workers

11年9月13日星期二

11年9月13日星期二

Web � Workers � 没有绑定UI线程

11年9月13日星期二

//in � pagevar � worker � = � new � Worker("process.js"); � worker.onmessage � = � function(event){

useData(event.data);worker.postMessage(values);

};

//in � process.jsself.onmessage � = � function(event){ � 

var � items � = � event.data; � for � (var � i=0,len=items.length; � i � < � len; � i++){};process(items[i]); � self.postMessage(items);

}11年9月13日星期二

使用场景

编码/解码大字符串

复杂数学运算

大数组排序

11年9月13日星期二

浏览器支持情况

7.0 3.5 ? 10.6 4.0

11年9月13日星期二

DOM编程

11年9月13日星期二

天生就慢

ECAM � Land DOM � Land

ECMA每次访问DOM,都需要途经这座桥,并交纳”过桥费”.访问的次数越多,费用就越高

11年9月13日星期二

代价很昂贵

function � innerHTMLLoop(){for(var � count=0;count<1000;i++){

document.getElementById(‘i’).innerHTML+=”a”;

}}

11年9月13日星期二

优化后

function � innerHTMLLoop(){var � content=[];for(var � count=0;count<1000;i++){

content.push(‘a’);}document.getElementById(‘i’).innerHTML � = � content.join(‘’);

}

11年9月13日星期二

比较

innerHTML,createElement,cloneNode

案例:http://blog.stevenlevithan.com/archives/faster-than-innerhtml

http://stevenlevithan.com/demo/replaceHtml.html

11年9月13日星期二

重绘和重排

11年9月13日星期二

UI � Update时间 � = � UI不可用时间

11年9月13日星期二

<button � id="btn" � style="font-size: � 30px;">Click � Me</button><script � type="text/javascript"> � 

window.onload � = � function(){document.getElementById("btn").onclick � = � function(){ � 

var � div � = � document.createElement(“div”); � div.className � = � “tip”; � div.innerHTML � = � “You � clicked � me!”; � document.body.appendChild(div);

}; � }

</script>

11年9月13日星期二

JavaScript执行会触发重绘和重排会导致长时间UI � Update

11年9月13日星期二

浏览器渲染

11年9月13日星期二

重绘何时发生?

visibility

颜色

背景图片

不会触发layout改变

11年9月13日星期二

重排何时发生?

添加或删除可见的DOM节点

元素位置改变

元素尺寸改变(包括:外边框,内边距,边框厚度,宽度,高度等属性改变)

页面渲染器初始化

浏览器窗口尺寸改变

页面布局+几何属性改变都会触发重排

11年9月13日星期二

重绘<button � id="btn">Click � Me</button><script � type="text/javascript"> � 

window.onload � = � function(){document.getElementById("btn").onclick � = � function(){ � 

this.style.color � = � "#ff0";}

}; � </script>

重绘

11年9月13日星期二

重排

<button � id="btn" � style="font-size: � 30px;">Click � Me</button><script � type="text/javascript"> � window.onload � = � function(){

document.getElementById("btn").onclick � = � function(){ � var � div � = � document.createElement(“div”); � div.className � = � “tip”; � div.innerHTML � = � “You � clicked � me!”; � document.body.appendChild(div);

}; � }</script>

重排

11年9月13日星期二

小化重绘和重排

11年9月13日星期二

方法

批量修改样式,采用cssTest或者改变className的方式

批量修改DOM,使DOM脱离文档

缓存布局信息

让元素脱离动画流

事件委托

...

11年9月13日星期二

批量修改样式

var el = document.createElementById(‘mydiv’);el.style.borderLeft = ”1px”;el.style.borderRight = ‘2px’;el.style.padding = ‘5px’;

重绘

重绘

重绘

3次重绘

11年9月13日星期二

优化后

var el = document.createElementById(‘mydiv’);el.style.cssText = ‘border-left:1px;border-right:2px;padding:5px;’;

1次重绘

11年9月13日星期二

还可以这么做

var el = document.createElementById(‘mydiv’);el.className = ‘active’;

1次重绘

11年9月13日星期二

使DOM脱离文档

隐藏元素,应用修改,重新显示

使用文档片段(document � fragment)在当前DOM之外构造一个子树,在把它拷回文档

将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素

11年9月13日星期二

1.隐藏元素var � ul � = � document.getElementById(‘myList’);

ul.style.display � = � ‘none’;

appendDataToElement(ul,data);

ul.style.display � = � ‘block’;

11年9月13日星期二

文档片段

var � fragment � = � document.createDocmentFragment();

appendDataToElement(fragment,data);

document.getElementById(‘myList’).appendChild(‘f

ragment’)

11年9月13日星期二

缓存布局信息

myElement.style.left � = � 1+myElement.offsetLeft

+’px’;

if(myElement.offsetLeft>500){

stopAnimation();

}

非常的低效

11年9月13日星期二

可以这么优化

current++

myElement.style.left � = � current+’px’;

if(myElement.offsetLeft>500){

stopAnimation();

}

11年9月13日星期二

让元素脱离动画流

使用绝对位置定位页面上的元素,将其脱离文档流

让元素动起来.当它扩大时,会临时覆盖部分页面.这个时候只会小区域的重绘

当动画结束时恢复定位,从而知会下移一次文档的其他位置

11年9月13日星期二

事件委托

场景:当页面中存在大量元素,而且每⼀一个都要⼀一次或多次绑定事件处理器.

11年9月13日星期二

事件委托

结果是可能会影响性能.每绑定一个事件处理器都是有代价的,它要么加重了页面负担.,要么增加了运行期的执行时间.需要访问和修改的DOM元素越多, � 应用程序也就越慢.

11年9月13日星期二

解决方案

事件冒泡

例:YAHOO.util.Event.delegate

http://developer.yahoo.com/yui/event/

11年9月13日星期二

更多编程实践

更高效的代码(条件预加载,延迟加载,位操作等)

正则表达式优化(例:Detail详情延迟加载)

数据缓存(JS对象缓存,Ajax数据缓存等)

GC和避免内存泄漏(http://www.aliued.cn/2010/09/19/gc-and-js-memory-leak.html)

CSS3动画

其他见(Extreme � JavaScript � Performance.pdf)

11年9月13日星期二

工具

JS执行效率测试工具

内存泄漏检测工具

...

11年9月13日星期二

�����F

谢谢11年9月13日星期二

top related