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

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

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

3,386

Published on

Published in: Technology, Design
1 Comment
9 Likes
Statistics
Notes
  • Thanks for sharing.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
3,386
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
44
Comments
1
Likes
9
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会阻塞页面解析 如何实现无阻塞脚本加载 依赖管理与无阻塞并行加载与顺序执行(LAB.js实现机 制) 无阻塞脚本加载潜在问题 为什么要谈谈JavaScript线程 为什么要谈谈同步与异步 浏览器线程消息模型 从Web Worker看浏览器的线程 NodeJS的单线程与多线程 5/7/2011 2
  • 3. 说到无阻塞脚本加载 你不得不知道的大牛是 Steve Souders At Google on web performance and open source initiatives. http://www.stevesouders.com/ 5/7/2011 3
  • 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
  • 5. 5/7/2011 5
  • 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
  • 7. 5/7/2011 7
  • 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
  • 9. 为什么script标签会阻塞页面解析 基于以上的历史原因,JavaScript线程与UI 共用同一进程,而且JS任务优先于UI任务 浏览器会在遇到Script标签的时候进行解析 和执行 如果Script是外链的话,还要等待下载 如果页面存在多个外链脚本,下载是串行 的 在IE8, Firefox3.5, Safari4和Chrome2开始允 许并行下载JavaScript文件,但是依然会阻 塞页面的其他资源的下载 5/7/2011 9
  • 10. Chromium解析流程 此图由Tapir(貘)提供,感谢他 5/7/2011 10
  • 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
  • 12. 无阻塞脚本加载 我们需要预先加载脚本,而且不阻塞UI的 渲染  对YSlow规则说No  Put Scripts at the Bottom 为过往的犯错买单,看看标准和浏览器厂 商都做了什么方案来补救 5/7/2011 12
  • 13. Defer属性 HTML4标准为<script>标签定义了defer属性, 以此声明告诉浏览器内容中不包含 document.write之类破坏DOM的脚本 浏览器会延迟(无阻塞)下载脚本,并按 <script>脚本顺序执行。 在onload事件前执行 实现/支持情况  IE4.0  Firefox3.5 5/7/2011 13
  • 14. Async属性 HTML5标准为<script>标签定义了async属性 与defer属性相同的是脚本会无阻塞加载 与defer属性不同的是脚本在加载完了立即 执行,并非按照<script>标签顺序执行 实现/支持情况  Firefox3.6  Opera10.5  Safari  Chrome  IE9.0(defer的别名?) 5/7/2011 14
  • 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
  • 17. 无阻塞脚本加载实例 Google Analytics代码  http://code.google.com/intl/zh- CN/apis/analytics/docs/tracking/asyncTracking.ht ml 5/7/2011 17
  • 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
  • 19. XMLHttpRequest Eval $.get(“script.js”, function (responseText) { eval(responseText); }); 与XMLHttpRequest具有相同的优点和缺点 但是,Eval is Evil。 5/7/2011 19
  • 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
  • 22. 问题总结 同源策略限制(XHR inject, XHR eval,Iframe) 顺序执行(动态script元素, async) 浏览器支持(async, defer) 并行下载与顺序执行 5/7/2011 22
  • 23. 各种方法支持一览 方法 并行加载 顺序执行 不受同源限制 浏览器都支持Defer √ √ √ xAsync √ x √ x动态脚本元素 √ x √ √XHR Inject √ √ x √XHR Eval √ √ x √Document.write √ √ √ xIframe √ ? x √ 5/7/2011 23
  • 24. 无顺序执行的要求情况下 方法 并行加载 顺序执行 不受同源限制 浏览器都支持Defer √ √ √ xAsync √ x √ x动态脚本元素 √ x √ √XHR Inject √ √ x √XHR Eval √ √ x √Document.write √ √ √ xIframe √ ? x √ 5/7/2011 24
  • 25. 5/7/2011 25
  • 26. 如何控制依赖加载 通过onload事件控制? loadScript(“file1.js”, function() { loadScript(“file2.js”, function() { loadScript(“file3.js”, function() { alert(“All ready.”); }); }); }); 这是串行加载的 5/7/2011 26
  • 27. 如何控制依赖加载 或者采用defer? <script src=“script1.js” defer></script> <script src=“script2.js” defer></script> <script src=“script3.js” defer></script> 显然浏览器支持不够 5/7/2011 27
  • 28. 如何控制依赖加载 更或者通过XHR动态加载,顺序执行 同源策略怎么办? 5/7/2011 28
  • 29. 如何控制依赖加载 在Server端组织好脚本顺序,并且combo成 一个文件 这是一个好主意!!! 但是总有你不能combo的文件,比如本地 化的资源文件 5/7/2011 29
  • 30. 依赖加载 ≠必须顺序加载 ≠ 串行加载 5/7/2011 30
  • 31. 顺序执行 ≠立即执行 5/7/2011 31
  • 32. 有没有一种方法 能实现并行下载 能实现顺序执行 能没有副作用 5/7/2011 32
  • 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
  • 38. LABjs API示意 5/7/2011 38
  • 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
  • 40. 题外话 后来,getify赢了 5/7/2011 40
  • 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
  • 43. 回顾 Combo脚本文件,以减少链接数 优先加载和无阻塞加载,实现界面快速响 应 采用LABjs做依赖管理,实现并行下载和顺 序执行 控制好访问DOM的代码,确保在 DOMContentLoaded后执行 5/7/2011 43
  • 44. 为什么要啰嗦浏览器线程 无阻塞加载讲得又不深入,还要啰嗦浏览 器线程 因为扯谈了这么多,目的只是将脚本的下 载线程脱离UI线程而已 设计JavaScript执行和UI渲染共用UI线程的 人上辈子是折翼的天使,伤不起 5/7/2011 44
  • 45. JavaScript线程怨念知多少 为什么JavaScript作为一门编程语言,却没 有多线程? 抱怨JavaScript没有多线程的程序员不是好 程序员 有多少人用setTimeout模拟过多线程 5/7/2011 45
  • 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
  • 48. 浏览器线程知多少 UI渲染线程/JavaScript执行线程 资源下载线程(JavaScript, CSS, Image, Object) Ajax线程 Web Worker线程 还有?欢迎补充 5/7/2011 48
  • 49. 前端,你伤不起 在所有下载线程中  为什么CSS文件加载不会block页面?  为什么图片加载不会block页面?  为什么flash加载不会block页面?  为什么ajax还有同步和异步之分?  为什么JavaScript文件会block UI? 5/7/2011 49
  • 50. 关于AJAX的同步异步 同步与异步的差别来自于AJAX的盛行 同步Ajax会锁住整个浏览器,直到请求完成 而异步请求则无影响,但是接收结果必须 通过回调函数 5/7/2011 50
  • 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
  • 52. 异步Ajax模型 5/7/2011 52
  • 53. 同步Ajax模型 5/7/2011 53
  • 54. 消息模型与事件驱动 在所有的浏览器线程之间的交互,是通过 消息来传递的 我们唯一能控制的线程是UI线程 其余的线程我们只能通过侦听事件消息才 能得到数据 所有的事件handler都由浏览器来做事件驱 动 5/7/2011 54
  • 55. 消息编程模型 Vs. 多线程编程 消息模型是构建在多线程的基础之上的 在JavaScript的领域里(包括NodeJS), 多线程 的细节被隔离 应用里的多线程操作通过事件驱动和消息 传递暴露给程序员 鼓吹消息模型而鄙视多线程模型的程序员 不是好的工程师 5/7/2011 55
  • 56. 从Web Worker看浏览器的线程 将JavaScript执行线程脱离UI线程 Web Worker的线程也可以控制,但是交互 依然是消息 5/7/2011 56
  • 57. 浏览器是最成熟的消息模型应用 异步Ajax 各种资源的下载消息通知 浏览器原生事件驱动  UI事件驱动  网络事件驱动 5/7/2011 57
  • 58. 消息模型的优势 编程模型简化  多线程框架由底层实现和托管,我们只负责调 用API启动线程,发送消息和侦听消息  对编程人员隔离多线程的烦恼 程序耦合性低  程序代码属于被trigger的状况,不耦合于目标 代码  我讨厌异步特有的callback编程,但是我喜欢消 息模型 原生的AOP编程 5/7/2011 58
  • 59. 消息模型的弱势 无法手动构造多线程实现并行和异步  严重依赖应用的支持  以web worker和websocket为例,只有浏览器支持 的情况下,才能享受到消息模型的好处 事件驱动依赖应用实现  可以手动触发自定义事件,但是对于特殊事件 无法触发 5/7/2011 59
  • 60. NodeJS是一个跑在Server端的浏览器 浏览器架构(Chrome) NodeJS架构 5/7/2011 60
  • 61. 未来世界也许会是这样的 它也许是浏览器 它也许是NodeJS 它也许什么应用都不是,但是应用都跑在 这上面 5/7/2011 61
  • 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
  • 64. 结语 Block UI的JavaScript不是好JavaScript 无消息模型,不JavaScript 无事件驱动,不JavaScript 浏览器是多线程的,JavaScript是单线程 上一条适用于NodeJS 5/7/2011 64
  • 65. References http://www.stevesouders.com/blog/2009/04/ 27/loading-scripts-without-blocking/ http://www.blog.highub.com/mobile- 2/mobile-safari-scripts-loading-without- blocking/ http://blogs.sitepoint.com/non-blocking- async-defer/ 5/7/2011 65
  • 66. Thanks 5/7/2011 66

×