More Related Content Similar to 从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
Similar to 从无阻塞并行脚本加载(Lab.js)到浏览器消息模型 (20) More from Jackson Tian (11) 从无阻塞并行脚本加载(Lab.js)到浏览器消息模型4. 为什么script标签会阻塞页面解
析
从Document.write说起
Document.write() 是魔鬼。
<p>task 1.</p>
<script>document.write(“<p>task5</p>”);</script>
<p>task 2.</p>
<p>task 3.</p>
<p>task 4.</p>
浏览器在UI渲染上,采用队列的方式实现
5/7/2011 4
6. Document.write是魔鬼
队列的基本原则
先进先出原则(FIFO, First-In-First-Out)
http://zh.wikipedia.org/wiki/%E9%98%9F%E5%8
8%97
Document.write破坏了原始的UI更新队列
从document.write来看,JavaScript设计之
初只是用来做Web页面辅助的
5/7/2011 6
8. Alert也是魔鬼
alert(“I am evil, too.”);
是否有人通过alert来debug JavaScript程序
呢?
for(var i=0; i< 1000; i++) {
alert(“Hi, ” + i);
}
alert最坏的地方在于它中断整个UI线程
用户体验的敌人(桌面浏览器)
confirm,prompt方法也是类似的
5/7/2011 8
11. YSlow的35条军规
Put Scripts at the Bottom
http://developer.yahoo.com/performance/rules.html#js_bottom
如果脚本的下载+解析+执行的时间太久,UI队列没有得到执行,
页面会出现空白
Yahoo!建议将所有的脚本都放在</body>之前,让UI队列优先
执行和显示。
Minimize HTTP Requests
http://developer.yahoo.com/performance/rules.html#num_http
页面脚本过多的情况下,通过combo和compress减少请求数
问题?
在</body>之前存在一个较大的脚本文件需要下载和执行
UI在ready之后,需要较长时间等待脚本的下载和执行,在脚本
ready之前,UI是出于无事件响应状态的。
5/7/2011 11
15. 标准与实现之间的差距
动态脚本元素
利用一小段脚本去创建<script>标签,实现无阻
塞加载
var script = document.createElement('script');
script.src = 'myscript.js';
var head =
document.getElementsByTagName('head')[0];
head.appendChild(script);
5/7/2011 15
16. Script标签的onload事件
function loadScript(url, callback){
var script = document.createElement(”script”)
script.type = “text/javascript”;
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == “loaded” ||
script.readyState == “complete”){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.body.appendChild(script);
}
5/7/2011 16
18. XMLHttprequest脚本注入
$.get(“script.js”, function (responseText) {
var script =
document.createElement(“script”);
script.type=“text/javascript”;
script.text = “responseText”;
document.body.appendChild(script);
});
可以控制下载和执行
该方法基于Ajax,受同源策略影响,无法使
用CDN。
5/7/2011 18
20. document.write Script Tag
document.write(“<script src=‘script.js’>
</script>”);
只有IE生效
如果在文档流关闭之后再调用
document.write(),整个DOM会被毁坏
5/7/2011 20
21. Script in Iframe
我也不知道怎么搞。
很复杂。
Google都Google不到。
也许是<iframe src=“#”></iframe>
5/7/2011 21
23. 各种方法支持一览
方法 并行加载 顺序执行 不受同源限制 浏览器都支持
Defer √ √ √ x
Async √ x √ x
动态脚本元素 √ x √ √
XHR Inject √ √ x √
XHR Eval √ √ x √
Document.write √ √ √ x
Iframe √ ? x √
5/7/2011 23
24. 无顺序执行的要求情况下
方法 并行加载 顺序执行 不受同源限制 浏览器都支持
Defer √ √ √ x
Async √ x √ x
动态脚本元素 √ x √ √
XHR Inject √ √ x √
XHR Eval √ √ x √
Document.write √ √ √ x
Iframe √ ? x √
5/7/2011 24
27. 如何控制依赖加载
或者采用defer?
<script src=“script1.js” defer></script>
<script src=“script2.js” defer></script>
<script src=“script3.js” defer></script>
显然浏览器支持不够
5/7/2011 27
30. 依赖加载
≠
必须顺序加载
≠
串行加载
5/7/2011 30
33. LAB.js
@getify
Kyle Simpson
公司:Getify Solutions
LABjs被很多大网站所采用,包括新版的
Twitter, Zappos, 以及 Vimeo.
Kyle Simpson在Steve Sounders的启示下写
下了LABjs这个库,用于管理并行加载和顺
序执行
5/7/2011 33
34. LABjs的诀窍
对于Firefox/Opera,采用动态Script DOM
element可以完美地实现并行下载和顺序执
行
对于Safari/Chrome,无法保证顺序执行,
但是LABjs通过插入一个<script
type=“text/cache” src=“#”>来实现。
IE/Safari/Chrome浏览器会下载文件到缓存
并触发onload事件,但不会执行
5/7/2011 34
35. LABjs的诀窍
在需要的文件下载完成之后,再次通过插
入正确的type=“text/javascript”而且监听
onload来完成对于顺序执行的控制
LABjs判断script文件是否同域文件,优先选
择XHR Injection实现并行下载,顺序执行
Text/cache严重依赖浏览器的非标准特性
5/7/2011 35
36. LABjs示例
Sample Code
<script src="LAB.js"></script>
<script>
$LAB .script("http://remote/jquery.js”).wait()
.script("/local/plugin1.jquery.js")
.script("/local/plugin2.jquery.js").wait()
.script("/local/init.js").wait(function(){ initMyPage();
});
</script>
5/7/2011 36
37. LABjs方法
主要方法
Script()
可以多次调用script方法来并行加载脚本
Wait()
通过调用wait方法来保证序列中的脚本执行顺序
5/7/2011 37
39. 题外话
曾经Firefox4试图改掉动态脚本元素按顺序执行的
特性,使得像Chrome/Safari一样
那么,在面对CDN脚本,text/cache失效的情况下,
LABjs会无可奈何地进行串行下载
有链接为证:
http://blog.getify.com/2010/10/ff4-script-loaders-and-
order-preservation/
http://blog.getify.com/2010/10/mozilla-labjs-the-story-
unfolds/
http://blog.getify.com/2010/10/mozilla-labjs-part-2/
5/7/2011 39
41. 无阻塞脚本与DOM
在脚本加载完成的时候,DOM可能
DOM还没有DOMContentLoaded(加载速度快或
者来自cache)
访问DOM是危险的
DOM可能已经DOMContentLoaded了
绑定DOMContentLoaded事件永远也不会被触发
如何判断DOM是否ready
DOM甚至已经Onload了(IE)
5/7/2011 41
42. 如何解决以上问题?
DOM还没有DOMContentLoaded(加载速度
快或者来自cache)
保证DOM操作注册在DOMContentLoaded事件
中
DOM可能已经DOMContentLoaded了
绑定DOMContentLoaded事件永远也不会被触发
通过document.readyState == “complete”判断
DOMready
DOM甚至已经Onload了(IE)
document.readyState 依然有效
5/7/2011 42
46. JavaScript线程
JavaScript的多线程
找到约 1,280,000 条结果 (用时 0.13 秒)
http://www.google.com.hk/search?sourceid=chro
me&ie=UTF-
8&q=JavaScript+%E5%A4%9A%E7%BA%BF%E7
%A8%8B
5/7/2011 46
47. 可是
你知道的
浏览器并不是单线程
JavaScript是单线程 ≠ 浏览器是单线程
WebApp ≠ JavaScript App
WebApp = 浏览器 + JavaScript + CSS + HTML +
Images + …. + API
所以
JavaScript的单线程是有原因的
5/7/2011 47
49. 前端,你伤不起
在所有下载线程中
为什么CSS文件加载不会block页面?
为什么图片加载不会block页面?
为什么flash加载不会block页面?
为什么ajax还有同步和异步之分?
为什么JavaScript文件会block UI?
5/7/2011 49
51. 被各种ajax库宠坏的孩子们
看看最原始的Ajax吧
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = function(){
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
// perfect!
} else {
// there was a problem with the request,
// for example the response may be a 404 (Not Found)
// or 500 (Internal Server Error) response codes
}
} else {
// still not ready
}
};
httpRequest.open('GET', ‘http://url', true);
httpRequest.send(null);
5/7/2011 51
55. 消息编程模型 Vs. 多线程编程
消息模型是构建在多线程的基础之上的
在JavaScript的领域里(包括NodeJS), 多线程
的细节被隔离
应用里的多线程操作通过事件驱动和消息
传递暴露给程序员
鼓吹消息模型而鄙视多线程模型的程序员
不是好的工程师
5/7/2011 55
58. 消息模型的优势
编程模型简化
多线程框架由底层实现和托管,我们只负责调
用API启动线程,发送消息和侦听消息
对编程人员隔离多线程的烦恼
程序耦合性低
程序代码属于被trigger的状况,不耦合于目标
代码
我讨厌异步特有的callback编程,但是我喜欢消
息模型
原生的AOP编程
5/7/2011 58
59. 消息模型的弱势
无法手动构造多线程实现并行和异步
严重依赖应用的支持
以web worker和websocket为例,只有浏览器支持
的情况下,才能享受到消息模型的好处
事件驱动依赖应用实现
可以手动触发自定义事件,但是对于特殊事件
无法触发
5/7/2011 59
62. 为何JScript在服务端会死掉?
var objConn = Server.CreateObject("ADODB.Connection");
// open our ADO connection and Execute the SQL command
objConn.Open("dsn=helpdesk", "sa", "");
objConn.Execute(strCommandText);
var returnErrCode =
objConn.Execute("SELECT @@ERROR as
errorCode").GetString();
objConn.Close();
objConn = null;
5/7/2011 62
63. JScript Vs. NodeJS
JScript之死
摒弃了JavaScript在浏览器中的异步机制
完全无视消息模型
无多线程支持
在Server端,表现与PHP和ASP无异
NodeJS之生
延续JavaScript在浏览器端赖以生存的特性
异步,消息模型,事件驱动
向程序员屏蔽多线程
5/7/2011 63