Your SlideShare is downloading. ×
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

从无阻塞并行脚本加载(Lab.js)到浏览器消息模型

2,408
views

Published on

web标准化交流会第十八期上海站分享。 …

web标准化交流会第十八期上海站分享。
Jackson Tian:从无阻塞并行脚本加载(LAB.js)到浏览器消息模型

Published in: Technology, Education

1 Comment
7 Likes
Statistics
Notes
No Downloads
Views
Total Views
2,408
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
36
Comments
1
Likes
7
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Jackson Tian/田永强 @朴灵 at SAP
    从无阻塞脚本加载(LAB.js)到浏览器线程模型
    5/7/2011
    1
  • 2. Agenda
    • 为什么script会阻塞页面解析
    • 3. 如何实现无阻塞脚本加载
    • 4. 依赖管理与无阻塞并行加载与顺序执行(LAB.js实现机制)
    • 5. 无阻塞脚本加载潜在问题
    • 6. 为什么要谈谈JavaScript线程
    • 7. 为什么要谈谈同步与异步
    • 8. 浏览器线程消息模型
    • 9. 从Web Worker看浏览器的线程
    • 10. NodeJS的单线程与多线程
    5/7/2011
    2
  • 11. 说到无阻塞脚本加载
    • 你不得不知道的大牛是
    • 12. Steve Souders
    • 13. At Google on web performance and open source initiatives.
    • 14. http://www.stevesouders.com/
    5/7/2011
    3
  • 15. 为什么script标签会阻塞页面解析
    • 从Document.write说起
    • 16. Document.write() 是魔鬼。
    • 17. <p>task 1.</p><script>document.write(“<p>task5</p>”);</script><p>task 2.</p><p>task 3.</p><p>task 4.</p>
    • 18. 浏览器在UI渲染上,采用队列的方式实现
    5/7/2011
    4
  • 19. 5/7/2011
    5
  • 20. Document.write是魔鬼
    • 队列的基本原则
    • 21. 先进先出原则(FIFO, First-In-First-Out)http://zh.wikipedia.org/wiki/%E9%98%9F%E5%88%97
    • 22. Document.write破坏了原始的UI更新队列
    • 23. 从document.write来看,JavaScript设计之初只是用来做Web页面辅助的
    5/7/2011
    6
  • 24. 5/7/2011
    7
  • 25. Alert也是魔鬼
    • alert(“I am evil, too.”);
    • 26. 是否有人通过alert来debug JavaScript程序呢?
    • 27. for(vari=0; i< 1000; i++) { alert(“Hi, ” + i);}
    • 28. alert最坏的地方在于它中断整个UI线程
    • 29. 用户体验的敌人(桌面浏览器)
    • 30. confirm,prompt方法也是类似的
    5/7/2011
    8
  • 31. 为什么script标签会阻塞页面解析
    • 基于以上的历史原因,JavaScript线程与UI共用同一进程,而且JS任务优先于UI任务
    • 32. 浏览器会在遇到Script标签的时候进行解析和执行
    • 33. 如果Script是外链的话,还要等待下载
    • 34. 如果页面存在多个外链脚本,下载是串行的
    • 35. 在IE8, Firefox3.5, Safari4和Chrome2开始允许并行下载JavaScript文件,但是依然会阻塞页面的其他资源的下载
    5/7/2011
    9
  • 36. 5/7/2011
    Chromium解析流程
    10
    此图由Tapir(貘)提供,感谢他
  • 37. YSlow的35条军规
    • Put Scripts at the Bottom
    • 38. http://developer.yahoo.com/performance/rules.html#js_bottom
    • 39. 如果脚本的下载+解析+执行的时间太久,UI队列没有得到执行,页面会出现空白
    • 40. Yahoo!建议将所有的脚本都放在</body>之前,让UI队列优先执行和显示。
    • 41. Minimize HTTP Requests
    • 42. http://developer.yahoo.com/performance/rules.html#num_http
    • 43. 页面脚本过多的情况下,通过combo和compress减少请求数
    • 44. 问题?
    • 45. 在</body>之前存在一个较大的脚本文件需要下载和执行
    • 46. UI在ready之后,需要较长时间等待脚本的下载和执行,在脚本ready之前,UI是出于无事件响应状态的。
    5/7/2011
    11
  • 47. 无阻塞脚本加载
    • 我们需要预先加载脚本,而且不阻塞UI的渲染
    • 48. 对YSlow规则说No
    • 49. Put Scripts at the Bottom
    • 50. 为过往的犯错买单,看看标准和浏览器厂商都做了什么方案来补救
    5/7/2011
    12
  • 51. Defer属性
    • HTML4标准为<script>标签定义了defer属性,以此声明告诉浏览器内容中不包含document.write之类破坏DOM的脚本
    • 52. 浏览器会延迟(无阻塞)下载脚本,并按<script>脚本顺序执行。
    • 53. 在onload事件前执行
    • 54. 实现/支持情况
    • 55. IE4.0
    • 56. Firefox3.5
    5/7/2011
    13
  • 57. Async属性
    • HTML5标准为<script>标签定义了async属性
    • 58. 与defer属性相同的是脚本会无阻塞加载
    • 59. 与defer属性不同的是脚本在加载完了立即执行,并非按照<script>标签顺序执行
    • 60. 实现/支持情况
    • 61. Firefox3.6
    • 62. Opera10.5
    • 63. Safari
    • 64. Chrome
    • 65. IE9.0(defer的别名?)
    5/7/2011
    14
  • 66. 标准与实现之间的差距
    • 动态脚本元素
    • 67. 利用一小段脚本去创建<script>标签,实现无阻塞加载
    • 68. var script = document.createElement('script'); script.src = 'myscript.js';var head = document.getElementsByTagName('head')[0];head.appendChild(script);
    5/7/2011
    15
  • 69. 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
  • 70. 无阻塞脚本加载实例
    • Google Analytics代码
    • 71. http://code.google.com/intl/zh-CN/apis/analytics/docs/tracking/asyncTracking.html
    5/7/2011
    17
  • 72. XMLHttprequest脚本注入
    • $.get(“script.js”, function (responseText) { var script = document.createElement(“script”); script.type=“text/javascript”; script.text = “responseText”; document.body.appendChild(script);});
    • 73. 可以控制下载和执行
    • 74. 该方法基于Ajax,受同源策略影响,无法使用CDN。
    5/7/2011
    18
  • 75. XMLHttpRequestEval
    • $.get(“script.js”, function (responseText) {eval(responseText);});
    • 76. 与XMLHttpRequest具有相同的优点和缺点
    • 77. 但是,Eval is Evil。
    5/7/2011
    19
  • 78. document.write Script Tag
    •  document.write(“<script src=‘script.js’> </script>”);
    • 79. 只有IE生效
    • 80. 如果在文档流关闭之后再调用document.write(),整个DOM会被毁坏
    5/7/2011
    20
  • 81. Script in Iframe
    • 我也不知道怎么搞。
    • 82. 很复杂。
    • 83. Google都Google不到。
    • 84. 也许是<iframe src=“#”></iframe>
    5/7/2011
    21
  • 85. 问题总结
    • 同源策略限制(XHR inject, XHR eval,Iframe)
    • 86. 顺序执行(动态script元素, async)
    • 87. 浏览器支持(async, defer)
    • 88. 并行下载与顺序执行
    5/7/2011
    22
  • 89. 各种方法支持一览
    5/7/2011
    23
  • 90. 无顺序执行的要求情况下
    5/7/2011
    24
  • 91. 5/7/2011
    25
  • 92. 如何控制依赖加载
    • 通过onload事件控制?loadScript(“file1.js”, function() {loadScript(“file2.js”, function() {loadScript(“file3.js”, function() { alert(“All ready.”); }); }); });
    • 93. 这是串行加载的
    5/7/2011
    26
  • 94. 如何控制依赖加载
    • 或者采用defer?<script src=“script1.js” defer></script> <script src=“script2.js” defer></script> <script src=“script3.js” defer></script>
    • 95. 显然浏览器支持不够
    5/7/2011
    27
  • 96. 如何控制依赖加载
    • 更或者通过XHR动态加载,顺序执行
    • 97. 同源策略怎么办?
    5/7/2011
    28
  • 98. 如何控制依赖加载
    • 在Server端组织好脚本顺序,并且combo成一个文件
    • 99. 这是一个好主意!!!
    • 100. 但是总有你不能combo的文件,比如本地化的资源文件
    5/7/2011
    29
  • 101. 依赖加载≠必须顺序加载 ≠串行加载
    5/7/2011
    30
  • 102. 顺序执行≠立即执行
    5/7/2011
    31
  • 103. 有没有一种方法
    • 能实现并行下载
    • 104. 能实现顺序执行
    • 105. 能没有副作用
    5/7/2011
    32
  • 106. LAB.js
    • @getify
    • 107. Kyle Simpson
    • 108. 公司:Getify Solutions
    • 109. LABjs被很多大网站所采用,包括新版的Twitter, Zappos, 以及Vimeo.
    • 110. Kyle Simpson在Steve Sounders的启示下写下了LABjs这个库,用于管理并行加载和顺序执行
    5/7/2011
    33
  • 111. LABjs的诀窍
    • 对于Firefox/Opera,采用动态Script DOM element可以完美地实现并行下载和顺序执行
    • 112. 对于Safari/Chrome,无法保证顺序执行,但是LABjs通过插入一个<script type=“text/cache” src=“#”>来实现。IE/Safari/Chrome浏览器会下载文件到缓存并触发onload事件,但不会执行
    5/7/2011
    34
  • 113.
    • 在需要的文件下载完成之后,再次通过插入正确的type=“text/javascript”而且监听onload来完成对于顺序执行的控制
    • 114. LABjs判断script文件是否同域文件,优先选择XHR Injection实现并行下载,顺序执行
    • 115. Text/cache严重依赖浏览器的非标准特性
    LABjs的诀窍
    5/7/2011
    35
  • 116. LABjs示例
    • Sample Code
    • 117. <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
  • 118. LABjs方法
    • 主要方法
    • 119. Script()
    • 120. 可以多次调用script方法来并行加载脚本
    • 121. Wait()
    • 122. 通过调用wait方法来保证序列中的脚本执行顺序
    5/7/2011
    37
  • 123. LABjs API示意
    5/7/2011
    38
  • 124. 题外话
    • 曾经Firefox4试图改掉动态脚本元素按顺序执行的特性,使得像Chrome/Safari一样
    • 125. 那么,在面对CDN脚本,text/cache失效的情况下,LABjs会无可奈何地进行串行下载
    • 126. 有链接为证:
    • 127. http://blog.getify.com/2010/10/ff4-script-loaders-and-order-preservation/
    • 128. http://blog.getify.com/2010/10/mozilla-labjs-the-story-unfolds/
    • 129. http://blog.getify.com/2010/10/mozilla-labjs-part-2/
    5/7/2011
    39
  • 130. 题外话
    • 后来,getify赢了
    5/7/2011
    40
  • 131. 无阻塞脚本与DOM
    • 在脚本加载完成的时候,DOM可能
    • 132. DOM还没有DOMContentLoaded(加载速度快或者来自cache)
    • 133. 访问DOM是危险的
    • 134. DOM可能已经DOMContentLoaded了
    • 135. 绑定DOMContentLoaded事件永远也不会被触发
    • 136. 如何判断DOM是否ready
    • 137. DOM甚至已经Onload了(IE)
    5/7/2011
    41
  • 138. 如何解决以上问题?
    • DOM还没有DOMContentLoaded(加载速度快或者来自cache)
    • 139. 保证DOM操作注册在DOMContentLoaded事件中
    • 140. DOM可能已经DOMContentLoaded了
    • 141. 绑定DOMContentLoaded事件永远也不会被触发
    • 142. 通过document.readyState == “complete”判断DOMready
    • 143. DOM甚至已经Onload了(IE)
    • 144. document.readyState 依然有效
    5/7/2011
    42
  • 145. 回顾
    • Combo脚本文件,以减少链接数
    • 146. 优先加载和无阻塞加载,实现界面快速响应
    • 147. 采用LABjs做依赖管理,实现并行下载和顺序执行
    • 148. 控制好访问DOM的代码,确保在DOMContentLoaded后执行
    5/7/2011
    43
  • 149. 为什么要啰嗦浏览器线程
    • 无阻塞加载讲得又不深入,还要啰嗦浏览器线程
    • 150. 因为扯谈了这么多,目的只是将脚本的下载线程脱离UI线程而已
    • 151. 设计JavaScript执行和UI渲染共用UI线程的人上辈子是折翼的天使,伤不起
    5/7/2011
    44
  • 152.
    • 为什么JavaScript作为一门编程语言,却没有多线程?
    • 153. 抱怨JavaScript没有多线程的程序员不是好程序员
    • 154. 有多少人用setTimeout模拟过多线程
    JavaScript线程怨念知多少
    5/7/2011
    45
  • 155. JavaScript线程
    • JavaScript的多线程
    • 156. 找到约 1,280,000 条结果 (用时 0.13 秒) 
    • 157. http://www.google.com.hk/search?sourceid=chrome&ie=UTF-8&q=JavaScript+%E5%A4%9A%E7%BA%BF%E7%A8%8B
    5/7/2011
    46
  • 158. 可是
    • 你知道的
    • 159. 浏览器并不是单线程
    • 160. JavaScript是单线程 ≠ 浏览器是单线程
    • 161. WebApp ≠ JavaScript App
    • 162. WebApp = 浏览器 + JavaScript + CSS + HTML + Images + …. + API
    • 163. 所以
    • 164. JavaScript的单线程是有原因的
    5/7/2011
    47
  • 165. 浏览器线程知多少
    • UI渲染线程/JavaScript执行线程
    • 166. 资源下载线程(JavaScript, CSS, Image, Object)
    • 167. Ajax线程
    • 168. Web Worker线程
    • 169. 还有?欢迎补充
    5/7/2011
    48
  • 170. 前端,你伤不起
    • 在所有下载线程中
    • 171. 为什么CSS文件加载不会block页面?
    • 172. 为什么图片加载不会block页面?
    • 173. 为什么flash加载不会block页面?
    • 174. 为什么ajax还有同步和异步之分?
    • 175. 为什么JavaScript文件会block UI?
    5/7/2011
    49
  • 176. 关于AJAX的同步异步
    • 同步与异步的差别来自于AJAX的盛行
    • 177. 同步Ajax会锁住整个浏览器,直到请求完成
    • 178. 而异步请求则无影响,但是接收结果必须通过回调函数
    5/7/2011
    50
  • 179. 被各种ajax库宠坏的孩子们
    • 看看最原始的Ajax吧
    • 180. varhttpRequest;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
  • 181. 5/7/2011
    异步Ajax模型
    52
  • 182. 5/7/2011
    同步Ajax模型
    53
  • 183. 消息模型与事件驱动
    • 在所有的浏览器线程之间的交互,是通过消息来传递的
    • 184. 我们唯一能控制的线程是UI线程
    • 185. 其余的线程我们只能通过侦听事件消息才能得到数据
    • 186. 所有的事件handler都由浏览器来做事件驱动
    5/7/2011
    54
  • 187. 消息编程模型 Vs. 多线程编程
    消息模型是构建在多线程的基础之上的
    在JavaScript的领域里(包括NodeJS), 多线程的细节被隔离
    应用里的多线程操作通过事件驱动和消息传递暴露给程序员
    鼓吹消息模型而鄙视多线程模型的程序员不是好的工程师
    5/7/2011
    55
  • 188. 从Web Worker看浏览器的线程
    • 将JavaScript执行线程脱离UI线程
    • 189. Web Worker的线程也可以控制,但是交互依然是消息
    5/7/2011
    56
  • 190. 5/7/2011
    浏览器是最成熟的消息模型应用
    异步Ajax
    各种资源的下载消息通知
    浏览器原生事件驱动
    UI事件驱动
    网络事件驱动
    57
  • 191. 5/7/2011
    消息模型的优势
    编程模型简化
    多线程框架由底层实现和托管,我们只负责调用API启动线程,发送消息和侦听消息
    对编程人员隔离多线程的烦恼
    程序耦合性低
    程序代码属于被trigger的状况,不耦合于目标代码
    我讨厌异步特有的callback编程,但是我喜欢消息模型
    原生的AOP编程
    58
  • 192. 5/7/2011
    消息模型的弱势
    无法手动构造多线程实现并行和异步
    严重依赖应用的支持
    以web worker和websocket为例,只有浏览器支持的情况下,才能享受到消息模型的好处
    事件驱动依赖应用实现
    可以手动触发自定义事件,但是对于特殊事件无法触发
    59
  • 193. NodeJS是一个跑在Server端的浏览器
    NodeJS架构
    浏览器架构(Chrome)
    5/7/2011
    60
  • 194. 5/7/2011
    未来世界也许会是这样的
    它也许是浏览器
    它也许是NodeJS
    它也许什么应用都不是,但是应用都跑在这上面
    61
  • 195. 5/7/2011
    为何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;
    62
  • 196. 5/7/2011
    JScript Vs. NodeJS
    JScript之死
    摒弃了JavaScript在浏览器中的异步机制
    完全无视消息模型
    无多线程支持
    在Server端,表现与PHP和ASP无异
    NodeJS之生
    延续JavaScript在浏览器端赖以生存的特性
    异步,消息模型,事件驱动
    向程序员屏蔽多线程
    63
  • 197. 5/7/2011
    结语
    Block UI的JavaScript不是好JavaScript
    无消息模型,不JavaScript
    无事件驱动,不JavaScript
    浏览器是多线程的,JavaScript是单线程
    上一条适用于NodeJS
    64
  • 198. References
    • http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
    • 199. http://www.blog.highub.com/mobile-2/mobile-safari-scripts-loading-without-blocking/
    • 200. http://blogs.sitepoint.com/non-blocking-async-defer/
    5/7/2011
    65
  • 201. 5/7/2011
    Thanks
    66