SlideShare a Scribd company logo
从无阻塞脚本加载 (LAB.js) 到浏览器线程模型 Jackson Tian/ 田永强  @ 朴灵  at SAP 05/09/11
Agenda 为什么 script 会阻塞页面解析 如何实现无阻塞脚本加载 依赖管理与无阻塞并行加载与顺序执行 (LAB.js 实现机制 ) 无阻塞脚本加载潜在问题 为什么要谈谈 JavaScript 线程 为什么要谈谈同步与异步 浏览器线程消息模型 从 Web Worker 看浏览器的线程 NodeJS 的单线程与多线程 05/09/11
说到无阻塞脚本加载 你不得不知道的大牛是 Steve Souders At  Google  on web performance and open source initiatives. http://www.stevesouders.com/ 05/09/11
为什么 script 标签会阻塞页面解析 05/09/11
05/09/11
Document.write 是魔鬼   队列的基本原则 先进先出原则( FIFO, First-In-First-Out ) http://zh.wikipedia.org/wiki/%E9%98%9F%E5%88%97   Document.write 破坏了原始的 UI 更新队列 从 document.write 来看, JavaScript 设计之初只是用来做 Web 页面辅助的 05/09/11
05/09/11
Alert 也是魔鬼 alert(“I am evil, too.”); 是否有人通过 alert 来 debug JavaScript 程序呢? for(var i=0; i< 1000; i++) { alert(“Hi, ” + i); } alert 最坏的地方在于它中断整个 UI 线程 用户体验的敌人 ( 桌面浏览器 ) confirm , prompt 方法也是类似的 05/09/11
为什么 script 标签会阻塞页面解析 基于以上的历史原因, JavaScript 线程与 UI 共用同一进程,而且 JS 任务优先于 UI 任务 浏览器会在遇到 Script 标签的时候进行解析和执行 如果 Script 是外链的话,还要等待下载 如果页面存在多个外链脚本,下载是串行的 在 IE8, Firefox3.5, Safari4 和 Chrome2 开始允许并行下载 JavaScript 文件,但是依然会阻塞页面的其他资源的下载 05/09/11
Chromium 解析流程 05/09/11 此图由 Tapir( 貘 ) 提供,感谢他
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 是出于无事件响应状态的。 05/09/11
无阻塞脚本加载 05/09/11
Defer 属性 HTML4 标准为 <script> 标签定义了 defer 属性,以此声明告诉浏览器内容中不包含 document.write 之类破坏 DOM 的脚本 浏览器会延迟(无阻塞)下载脚本,并按 <script> 脚本顺序执行。 在 onload 事件前执行 实现 / 支持情况 IE4.0 Firefox3.5 05/09/11
Async 属性 HTML5 标准为 <script> 标签定义了 async 属性 与 defer 属性相同的是脚本会无阻塞加载 与 defer 属性不同的是脚本在加载完了立即执行,并非按照 <script> 标签顺序执行 实现 / 支持情况 Firefox3.6 Opera10.5 Safari Chrome IE9.0 ( defer 的别名?) 05/09/11
标准与实现之间的差距 动态脚本元素 利用一小段脚本去创建 <script> 标签,实现无阻塞加载 var script = document.createElement('script');  script.src = 'myscript.js'; var head = document.getElementsByTagName('head')[0]; head.appendChild(script); 05/09/11
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); } 05/09/11
无阻塞脚本加载实例 Google Analytics 代码 http://code.google.com/intl/zh-CN/apis/analytics/docs/tracking/asyncTracking.html 05/09/11
XMLHttprequest 脚本注入 $.get(“script.js”, function (responseText) { var script = document.createElement(“script”); script.type=“text/javascript”; script.text = “responseText”; document.body.appendChild(script); }); 可以控制下载和执行 该方法基于 Ajax ,受同源策略影响,无法使用 CDN 。 05/09/11
XMLHttpRequest   Eval $.get(“script.js”, function (responseText) { eval(responseText); }); 与 XMLHttpRequest 具有相同的优点和缺点 但是, Eval is Evil 。 05/09/11
document.write Script Tag   document.write(“<script src=‘script.js’> </script>”); 只有 IE 生效 如果在文档流关闭之后再调用 document.write() ,整个 DOM 会被毁坏 05/09/11
Script in Iframe 我也不知道怎么搞。 很复杂。 Google 都 Google 不到。 也许是 <iframe src=“#”></iframe> 05/09/11
问题总结 同源策略限制 (XHR inject, XHR eval,Iframe) 顺序执行 ( 动态 script 元素 , async) 浏览器支持 (async, defer) 并行下载与顺序执行 05/09/11
各种方法支持一览 05/09/11 方法 并行加载 顺序执行 不受同源限制 浏览器都支持 Defer √ √ √ x Async √ x √ x 动态脚本元素 √ x √ √ XHR Inject √ √ x √ XHR Eval √ √ x √ Document.write √ √ √ x Iframe √ ? x √
无顺序执行的要求情况下 05/09/11 方法 并行加载 顺序执行 不受同源限制 浏览器都支持 Defer √ √ √ x Async √ x √ x 动态脚本元素 √ x √ √ XHR Inject √ √ x √ XHR Eval √ √ x √ Document.write √ √ √ x Iframe √ ? x √
05/09/11
如何控制依赖加载 通过 onload 事件控制? loadScript(“file1.js”, function() {   loadScript(“file2.js”, function() {   loadScript(“file3.js”, function() {   alert(“All ready.”);   });    });  }); 这是串行加载的 05/09/11
如何控制依赖加载 或者采用 defer ? <script src=“script1.js” defer></script>  <script src=“script2.js” defer></script>  <script src=“script3.js” defer></script> 显然浏览器支持不够 05/09/11
如何控制依赖加载 更或者通过 XHR 动态加载,顺序执行 同源策略怎么办? 05/09/11
如何控制依赖加载 在 Server 端组织好脚本顺序,并且 combo 成一个文件 这是一个好主意!!! 但是总有你不能 combo 的文件,比如本地化的资源文件 05/09/11
依赖加载  ≠ 必须顺序加载  ≠ 串行加载 05/09/11
顺序执行 ≠ 立即执行 05/09/11
有没有一种方法 能实现并行下载 能实现顺序执行 能没有副作用 05/09/11
LAB.js @getify Kyle Simpson 公司: Getify Solutions LABjs 被很多大网站所采用,包括新版的 Twitter ,  Zappos ,  以及  Vimeo . Kyle Simpson 在 Steve Sounders 的启示下写下了 LABjs 这个库,用于管理并行加载和顺序执行 05/09/11
LABjs 的诀窍 对于 Firefox/Opera ,采用动态 Script DOM element 可以完美地实现并行下载和顺序执行 对于 Safari/Chrome ,无法保证顺序执行,但是 LABjs 通过插入一个 <script type=“text/cache” src=“#”> 来实现。 IE/Safari/Chrome 浏览器会下载文件到缓存并触发 onload 事件,但不会执行 05/09/11
在需要的文件下载完成之后,再次通过插入正确的 type=“text/javascript” 而且监听 onload 来完成对于顺序执行的控制 LABjs 判断 script 文件是否同域文件,优先选择 XHR Injection 实现并行下载,顺序执行 Text/cache 严重依赖浏览器的非标准特性 LABjs 的诀窍 05/09/11
LABjs 示例 Sample Code <script src=&quot;LAB.js&quot;></script>  <script> $LAB .script(&quot;http://remote/jquery.js”).wait() .script(&quot;/local/plugin1.jquery.js&quot;) .script(&quot;/local/plugin2.jquery.js&quot;).wait() .script(&quot;/local/init.js&quot;).wait(function(){  initMyPage();  });  </script> 05/09/11
LABjs 方法 主要方法 Script() 可以多次调用 script 方法来并行加载脚本 Wait() 通过调用 wait 方法来保证序列中的脚本执行顺序 05/09/11
LABjs API 示意 05/09/11
题外话 曾经 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/ 05/09/11
题外话 后来, getify 赢了 05/09/11
无阻塞脚本与 DOM 在脚本加载完成的时候, DOM 可能 DOM 还没有 DOMContentLoaded( 加载速度快或者来自 cache) 访问 DOM 是危险的 DOM 可能已经 DOMContentLoaded 了 绑定 DOMContentLoaded 事件永远也不会被触发 如何判断 DOM 是否 ready DOM 甚至已经 Onload 了 (IE) 05/09/11
如何解决以上问题? DOM 还没有 DOMContentLoaded( 加载速度快或者来自 cache) 保证 DOM 操作注册在 DOMContentLoaded 事件中 DOM 可能已经 DOMContentLoaded 了 绑定 DOMContentLoaded 事件永远也不会被触发 通过 document.readyState == “complete” 判断 DOMready DOM 甚至已经 Onload 了 (IE) document.readyState  依然有效 05/09/11
回顾 Combo 脚本文件,以减少链接数 优先加载和无阻塞加载,实现界面快速响应 采用 LABjs 做依赖管理,实现并行下载和顺序执行 控制好访问 DOM 的代码,确保在 DOMContentLoaded 后执行 05/09/11
为什么要啰嗦浏览器线程 无阻塞加载讲得又不深入,还要啰嗦浏览器线程 因为扯谈了这么多,目的只是将脚本的下载线程脱离 UI 线程而已 设计 JavaScript 执行和 UI 渲染共用 UI 线程的人上辈子是折翼的天使,伤不起 05/09/11
为什么 JavaScript 作为一门编程语言,却没有多线程? 抱怨 JavaScript 没有多线程的程序员不是好程序员 有多少人用 setTimeout 模拟过多线程 JavaScript 线程怨念知多少 05/09/11
JavaScript 线程 JavaScript 的多线程 找到约  1,280,000  条结果 (用时  0.13  秒)  http://www.google.com.hk/search?sourceid=chrome&ie=UTF-8&q=JavaScript+%E5%A4%9A%E7%BA%BF%E7%A8%8B 05/09/11
可是 你知道的 浏览器并不是单线程 JavaScript 是单线程 ≠ 浏览器是单线程 WebApp ≠ JavaScript App WebApp =  浏览器  + JavaScript + CSS + HTML + Images + …. + API 所以 JavaScript 的单线程是有原因的 05/09/11
浏览器线程知多少 UI 渲染线程 /JavaScript 执行线程 资源下载线程 (JavaScript, CSS, Image, Object) Ajax 线程 Web Worker 线程 还有?欢迎补充 05/09/11
前端,你伤不起 在所有下载线程中 为什么 CSS 文件加载不会 block 页面? 为什么图片加载不会 block 页面? 为什么 flash 加载不会 block 页面? 为什么 ajax 还有同步和异步之分? 为什么 JavaScript 文件会 block UI ? 05/09/11
关于 AJAX 的同步异步 同步与异步的差别来自于 AJAX 的盛行 同步 Ajax 会锁住整个浏览器,直到请求完成 而异步请求则无影响,但是接收结果必须通过回调函数 05/09/11
被各种 ajax 库宠坏的孩子们 看看最原始的 Ajax 吧 var httpRequest; if (window.XMLHttpRequest) { // Mozilla, Safari, ...    httpRequest = new XMLHttpRequest();  } else if (window.ActiveXObject) { // IE    httpRequest = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);  } 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); 05/09/11
异步 Ajax 模型 05/09/11
同步 Ajax 模型 05/09/11
消息模型与事件驱动 在所有的浏览器线程之间的交互,是通过消息来传递的 我们唯一能控制的线程是 UI 线程 其余的线程我们只能通过侦听事件消息才能得到数据 所有的事件 handler 都由浏览器来做事件驱动 05/09/11
消息编程模型  Vs.  多线程编程 消息模型是构建在多线程的基础之上的 在 JavaScript 的领域里 ( 包括 NodeJS),  多线程的细节被隔离 应用里的多线程操作通过事件驱动和消息传递暴露给程序员 鼓吹消息模型而鄙视多线程模型的程序员不是好的工程师 05/09/11
从 Web Worker 看浏览器的线程 将 JavaScript 执行线程脱离 UI 线程 Web Worker 的线程也可以控制,但是交互依然是消息 05/09/11
浏览器是最成熟的消息模型应用 异步 Ajax 各种资源的下载消息通知 浏览器原生事件驱动 UI 事件驱动 网络事件驱动 05/09/11
消息模型的优势 编程模型简化 多线程框架由底层实现和托管,我们只负责调用 API 启动线程,发送消息和侦听消息 对编程人员隔离多线程的烦恼 程序耦合性低 程序代码属于被 trigger 的状况,不耦合于目标代码 我讨厌异步特有的 callback 编程,但是我喜欢消息模型 原生的 AOP 编程 05/09/11
消息模型的弱势 无法手动构造多线程实现并行和异步 严重依赖应用的支持 以 web worker 和 websocket 为例,只有浏览器支持的情况下,才能享受到消息模型的好处 事件驱动依赖应用实现 可以手动触发自定义事件,但是对于特殊事件无法触发 05/09/11
NodeJS 是一个跑在 Server 端的浏览器 浏览器架构 (Chrome) NodeJS 架构 05/09/11
未来世界也许会是这样的 它也许是浏览器 它也许是 NodeJS 它也许什么应用都不是,但是应用都跑在这上面 05/09/11
为何 JScript 在服务端会死掉? var objConn = Server.CreateObject(&quot;ADODB.Connection&quot;); // open our ADO connection and Execute the SQL command objConn.Open(&quot;dsn=helpdesk&quot;, &quot;sa&quot;, &quot;&quot;); objConn.Execute(strCommandText); var returnErrCode =  objConn.Execute(&quot;SELECT @@ERROR as errorCode&quot;).GetString(); objConn.Close(); objConn = null; 05/09/11
JScript Vs. NodeJS JScript 之死 摒弃了 JavaScript 在浏览器中的异步机制 完全无视消息模型 无多线程支持 在 Server 端,表现与 PHP 和 ASP 无异 NodeJS 之生 延续 JavaScript 在浏览器端赖以生存的特性 异步,消息模型,事件驱动 向程序员屏蔽多线程 05/09/11
结语 Block UI 的 JavaScript 不是好 JavaScript 无消息模型,不 JavaScript 无事件驱动,不 JavaScript 浏览器是多线程的, JavaScript 是单线程 上一条适用于 NodeJS 05/09/11
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/ 05/09/11
05/09/11 Thanks

More Related Content

PDF
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
PDF
Node.js 入門 - 前端工程開發實務訓練
PPTX
模块化和组件化Css
PDF
前端工程開發實務訓練
PPT
Jsp
PDF
Javascript 入門 - 前端工程開發實務訓練
PDF
TypeScript-twmvc#16
PDF
專案分層架構 twMVC#18
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
Node.js 入門 - 前端工程開發實務訓練
模块化和组件化Css
前端工程開發實務訓練
Jsp
Javascript 入門 - 前端工程開發實務訓練
TypeScript-twmvc#16
專案分層架構 twMVC#18

What's hot (19)

PDF
Asp.net mvc網站的從無到有
PDF
Powercli
PDF
twMVC#21 | 以實例說明ASP.NET Web API 服務的開發與測試過程
PDF
Spring 2.0 技術手冊第九章 - API 封裝
PDF
Spring 2.0 技術手冊第八章 - View 層方案、Web 框架整合
PDF
Angular js twmvc#17
PPTX
Asp.net mvc 概觀介紹
PDF
twMVC#19 | opserver監控服務的解決
PDF
ASP.NET MVC之實戰架構探討 -twMVC#5
PDF
ASP.NET MVC Model 的設計與使用 twMVC#10
PPTX
Entity framework + Linq 介紹
PDF
twMVC#19 | 微信公眾平台申請與wechat api 開發血淚史
PDF
YUI 教學 - 前端工程開發實務訓練
PDF
Debugging - 前端工程開發實務訓練
PDF
ASP.NET Core 2.1設計新思維與新發展
PPT
Dynamic JS Loader
PDF
Spring 2.0 技術手冊第五章 - JDBC、交易支援
PPTX
利用Signalr打造即時通訊@Tech day geek
DOC
四天学会Ajax
Asp.net mvc網站的從無到有
Powercli
twMVC#21 | 以實例說明ASP.NET Web API 服務的開發與測試過程
Spring 2.0 技術手冊第九章 - API 封裝
Spring 2.0 技術手冊第八章 - View 層方案、Web 框架整合
Angular js twmvc#17
Asp.net mvc 概觀介紹
twMVC#19 | opserver監控服務的解決
ASP.NET MVC之實戰架構探討 -twMVC#5
ASP.NET MVC Model 的設計與使用 twMVC#10
Entity framework + Linq 介紹
twMVC#19 | 微信公眾平台申請與wechat api 開發血淚史
YUI 教學 - 前端工程開發實務訓練
Debugging - 前端工程開發實務訓練
ASP.NET Core 2.1設計新思維與新發展
Dynamic JS Loader
Spring 2.0 技術手冊第五章 - JDBC、交易支援
利用Signalr打造即時通訊@Tech day geek
四天学会Ajax
Ad

Viewers also liked (6)

PPTX
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
PDF
彪叔 [2010]webrebuild
PDF
白玉磊 Webrebuild
PPT
QQ聊天系统后台架构的演化与启示
PDF
web标准化交流会bobby分享
PDF
web标准化交流会上海站bobby分享
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
彪叔 [2010]webrebuild
白玉磊 Webrebuild
QQ聊天系统后台架构的演化与启示
web标准化交流会bobby分享
web标准化交流会上海站bobby分享
Ad

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

PPT
钟志 第八期Web标准化交流会
DOCX
Javascript加载总结
PPTX
Lazyload实践
PPTX
浅析浏览器解析和渲染
PPTX
第三方内容开发最佳实践
PDF
Javascript autoload
PDF
HTML5 Web workers
钟志 第八期Web标准化交流会
Javascript加载总结
Lazyload实践
浅析浏览器解析和渲染
第三方内容开发最佳实践
Javascript autoload
HTML5 Web workers

More from 裕波 周 (13)

PPT
前端架构漫谈
PPT
Scrum敏捷项目管理
PPTX
移动设备web重构
PPTX
重温网站重构
PPT
从问题开始,前端,架构、框架与库的实战
PPTX
从问题开始,谈前端架构
PPTX
Ie9 overview
PPTX
《一专多长》——Twinsen[梁璟彪]
PPT
浏览器兼容性问题简介 --- 黄昊
PPT
揭秘Html5和Css3 ---- 鲁超伍
PPTX
网站重构Whoami ---- 偷米饭
PDF
分层语义化模板实践 ---- 张克军
PPT
javascript的分层概念 --- 阿当
前端架构漫谈
Scrum敏捷项目管理
移动设备web重构
重温网站重构
从问题开始,前端,架构、框架与库的实战
从问题开始,谈前端架构
Ie9 overview
《一专多长》——Twinsen[梁璟彪]
浏览器兼容性问题简介 --- 黄昊
揭秘Html5和Css3 ---- 鲁超伍
网站重构Whoami ---- 偷米饭
分层语义化模板实践 ---- 张克军
javascript的分层概念 --- 阿当

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

  • 1. 从无阻塞脚本加载 (LAB.js) 到浏览器线程模型 Jackson Tian/ 田永强 @ 朴灵 at SAP 05/09/11
  • 2. Agenda 为什么 script 会阻塞页面解析 如何实现无阻塞脚本加载 依赖管理与无阻塞并行加载与顺序执行 (LAB.js 实现机制 ) 无阻塞脚本加载潜在问题 为什么要谈谈 JavaScript 线程 为什么要谈谈同步与异步 浏览器线程消息模型 从 Web Worker 看浏览器的线程 NodeJS 的单线程与多线程 05/09/11
  • 3. 说到无阻塞脚本加载 你不得不知道的大牛是 Steve Souders At  Google  on web performance and open source initiatives. http://www.stevesouders.com/ 05/09/11
  • 6. Document.write 是魔鬼 队列的基本原则 先进先出原则( FIFO, First-In-First-Out ) http://zh.wikipedia.org/wiki/%E9%98%9F%E5%88%97 Document.write 破坏了原始的 UI 更新队列 从 document.write 来看, JavaScript 设计之初只是用来做 Web 页面辅助的 05/09/11
  • 8. Alert 也是魔鬼 alert(“I am evil, too.”); 是否有人通过 alert 来 debug JavaScript 程序呢? for(var i=0; i< 1000; i++) { alert(“Hi, ” + i); } alert 最坏的地方在于它中断整个 UI 线程 用户体验的敌人 ( 桌面浏览器 ) confirm , prompt 方法也是类似的 05/09/11
  • 9. 为什么 script 标签会阻塞页面解析 基于以上的历史原因, JavaScript 线程与 UI 共用同一进程,而且 JS 任务优先于 UI 任务 浏览器会在遇到 Script 标签的时候进行解析和执行 如果 Script 是外链的话,还要等待下载 如果页面存在多个外链脚本,下载是串行的 在 IE8, Firefox3.5, Safari4 和 Chrome2 开始允许并行下载 JavaScript 文件,但是依然会阻塞页面的其他资源的下载 05/09/11
  • 10. Chromium 解析流程 05/09/11 此图由 Tapir( 貘 ) 提供,感谢他
  • 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 是出于无事件响应状态的。 05/09/11
  • 13. Defer 属性 HTML4 标准为 <script> 标签定义了 defer 属性,以此声明告诉浏览器内容中不包含 document.write 之类破坏 DOM 的脚本 浏览器会延迟(无阻塞)下载脚本,并按 <script> 脚本顺序执行。 在 onload 事件前执行 实现 / 支持情况 IE4.0 Firefox3.5 05/09/11
  • 14. Async 属性 HTML5 标准为 <script> 标签定义了 async 属性 与 defer 属性相同的是脚本会无阻塞加载 与 defer 属性不同的是脚本在加载完了立即执行,并非按照 <script> 标签顺序执行 实现 / 支持情况 Firefox3.6 Opera10.5 Safari Chrome IE9.0 ( defer 的别名?) 05/09/11
  • 15. 标准与实现之间的差距 动态脚本元素 利用一小段脚本去创建 <script> 标签,实现无阻塞加载 var script = document.createElement('script'); script.src = 'myscript.js'; var head = document.getElementsByTagName('head')[0]; head.appendChild(script); 05/09/11
  • 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); } 05/09/11
  • 17. 无阻塞脚本加载实例 Google Analytics 代码 http://code.google.com/intl/zh-CN/apis/analytics/docs/tracking/asyncTracking.html 05/09/11
  • 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 。 05/09/11
  • 19. XMLHttpRequest Eval $.get(“script.js”, function (responseText) { eval(responseText); }); 与 XMLHttpRequest 具有相同的优点和缺点 但是, Eval is Evil 。 05/09/11
  • 20. document.write Script Tag   document.write(“<script src=‘script.js’> </script>”); 只有 IE 生效 如果在文档流关闭之后再调用 document.write() ,整个 DOM 会被毁坏 05/09/11
  • 21. Script in Iframe 我也不知道怎么搞。 很复杂。 Google 都 Google 不到。 也许是 <iframe src=“#”></iframe> 05/09/11
  • 22. 问题总结 同源策略限制 (XHR inject, XHR eval,Iframe) 顺序执行 ( 动态 script 元素 , async) 浏览器支持 (async, defer) 并行下载与顺序执行 05/09/11
  • 23. 各种方法支持一览 05/09/11 方法 并行加载 顺序执行 不受同源限制 浏览器都支持 Defer √ √ √ x Async √ x √ x 动态脚本元素 √ x √ √ XHR Inject √ √ x √ XHR Eval √ √ x √ Document.write √ √ √ x Iframe √ ? x √
  • 24. 无顺序执行的要求情况下 05/09/11 方法 并行加载 顺序执行 不受同源限制 浏览器都支持 Defer √ √ √ x Async √ x √ x 动态脚本元素 √ x √ √ XHR Inject √ √ x √ XHR Eval √ √ x √ Document.write √ √ √ x Iframe √ ? x √
  • 26. 如何控制依赖加载 通过 onload 事件控制? loadScript(“file1.js”, function() { loadScript(“file2.js”, function() { loadScript(“file3.js”, function() { alert(“All ready.”); }); }); }); 这是串行加载的 05/09/11
  • 27. 如何控制依赖加载 或者采用 defer ? <script src=“script1.js” defer></script> <script src=“script2.js” defer></script> <script src=“script3.js” defer></script> 显然浏览器支持不够 05/09/11
  • 28. 如何控制依赖加载 更或者通过 XHR 动态加载,顺序执行 同源策略怎么办? 05/09/11
  • 29. 如何控制依赖加载 在 Server 端组织好脚本顺序,并且 combo 成一个文件 这是一个好主意!!! 但是总有你不能 combo 的文件,比如本地化的资源文件 05/09/11
  • 30. 依赖加载 ≠ 必须顺序加载 ≠ 串行加载 05/09/11
  • 33. LAB.js @getify Kyle Simpson 公司: Getify Solutions LABjs 被很多大网站所采用,包括新版的 Twitter ,  Zappos , 以及 Vimeo . Kyle Simpson 在 Steve Sounders 的启示下写下了 LABjs 这个库,用于管理并行加载和顺序执行 05/09/11
  • 34. LABjs 的诀窍 对于 Firefox/Opera ,采用动态 Script DOM element 可以完美地实现并行下载和顺序执行 对于 Safari/Chrome ,无法保证顺序执行,但是 LABjs 通过插入一个 <script type=“text/cache” src=“#”> 来实现。 IE/Safari/Chrome 浏览器会下载文件到缓存并触发 onload 事件,但不会执行 05/09/11
  • 35. 在需要的文件下载完成之后,再次通过插入正确的 type=“text/javascript” 而且监听 onload 来完成对于顺序执行的控制 LABjs 判断 script 文件是否同域文件,优先选择 XHR Injection 实现并行下载,顺序执行 Text/cache 严重依赖浏览器的非标准特性 LABjs 的诀窍 05/09/11
  • 36. LABjs 示例 Sample Code <script src=&quot;LAB.js&quot;></script> <script> $LAB .script(&quot;http://remote/jquery.js”).wait() .script(&quot;/local/plugin1.jquery.js&quot;) .script(&quot;/local/plugin2.jquery.js&quot;).wait() .script(&quot;/local/init.js&quot;).wait(function(){ initMyPage(); }); </script> 05/09/11
  • 37. LABjs 方法 主要方法 Script() 可以多次调用 script 方法来并行加载脚本 Wait() 通过调用 wait 方法来保证序列中的脚本执行顺序 05/09/11
  • 38. LABjs API 示意 05/09/11
  • 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/ 05/09/11
  • 40. 题外话 后来, getify 赢了 05/09/11
  • 41. 无阻塞脚本与 DOM 在脚本加载完成的时候, DOM 可能 DOM 还没有 DOMContentLoaded( 加载速度快或者来自 cache) 访问 DOM 是危险的 DOM 可能已经 DOMContentLoaded 了 绑定 DOMContentLoaded 事件永远也不会被触发 如何判断 DOM 是否 ready DOM 甚至已经 Onload 了 (IE) 05/09/11
  • 42. 如何解决以上问题? DOM 还没有 DOMContentLoaded( 加载速度快或者来自 cache) 保证 DOM 操作注册在 DOMContentLoaded 事件中 DOM 可能已经 DOMContentLoaded 了 绑定 DOMContentLoaded 事件永远也不会被触发 通过 document.readyState == “complete” 判断 DOMready DOM 甚至已经 Onload 了 (IE) document.readyState 依然有效 05/09/11
  • 43. 回顾 Combo 脚本文件,以减少链接数 优先加载和无阻塞加载,实现界面快速响应 采用 LABjs 做依赖管理,实现并行下载和顺序执行 控制好访问 DOM 的代码,确保在 DOMContentLoaded 后执行 05/09/11
  • 44. 为什么要啰嗦浏览器线程 无阻塞加载讲得又不深入,还要啰嗦浏览器线程 因为扯谈了这么多,目的只是将脚本的下载线程脱离 UI 线程而已 设计 JavaScript 执行和 UI 渲染共用 UI 线程的人上辈子是折翼的天使,伤不起 05/09/11
  • 45. 为什么 JavaScript 作为一门编程语言,却没有多线程? 抱怨 JavaScript 没有多线程的程序员不是好程序员 有多少人用 setTimeout 模拟过多线程 JavaScript 线程怨念知多少 05/09/11
  • 46. JavaScript 线程 JavaScript 的多线程 找到约 1,280,000 条结果 (用时 0.13 秒)  http://www.google.com.hk/search?sourceid=chrome&ie=UTF-8&q=JavaScript+%E5%A4%9A%E7%BA%BF%E7%A8%8B 05/09/11
  • 47. 可是 你知道的 浏览器并不是单线程 JavaScript 是单线程 ≠ 浏览器是单线程 WebApp ≠ JavaScript App WebApp = 浏览器 + JavaScript + CSS + HTML + Images + …. + API 所以 JavaScript 的单线程是有原因的 05/09/11
  • 48. 浏览器线程知多少 UI 渲染线程 /JavaScript 执行线程 资源下载线程 (JavaScript, CSS, Image, Object) Ajax 线程 Web Worker 线程 还有?欢迎补充 05/09/11
  • 49. 前端,你伤不起 在所有下载线程中 为什么 CSS 文件加载不会 block 页面? 为什么图片加载不会 block 页面? 为什么 flash 加载不会 block 页面? 为什么 ajax 还有同步和异步之分? 为什么 JavaScript 文件会 block UI ? 05/09/11
  • 50. 关于 AJAX 的同步异步 同步与异步的差别来自于 AJAX 的盛行 同步 Ajax 会锁住整个浏览器,直到请求完成 而异步请求则无影响,但是接收结果必须通过回调函数 05/09/11
  • 51. 被各种 ajax 库宠坏的孩子们 看看最原始的 Ajax 吧 var httpRequest; if (window.XMLHttpRequest) { // Mozilla, Safari, ... httpRequest = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE httpRequest = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;); } 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); 05/09/11
  • 52. 异步 Ajax 模型 05/09/11
  • 53. 同步 Ajax 模型 05/09/11
  • 54. 消息模型与事件驱动 在所有的浏览器线程之间的交互,是通过消息来传递的 我们唯一能控制的线程是 UI 线程 其余的线程我们只能通过侦听事件消息才能得到数据 所有的事件 handler 都由浏览器来做事件驱动 05/09/11
  • 55. 消息编程模型 Vs. 多线程编程 消息模型是构建在多线程的基础之上的 在 JavaScript 的领域里 ( 包括 NodeJS), 多线程的细节被隔离 应用里的多线程操作通过事件驱动和消息传递暴露给程序员 鼓吹消息模型而鄙视多线程模型的程序员不是好的工程师 05/09/11
  • 56. 从 Web Worker 看浏览器的线程 将 JavaScript 执行线程脱离 UI 线程 Web Worker 的线程也可以控制,但是交互依然是消息 05/09/11
  • 57. 浏览器是最成熟的消息模型应用 异步 Ajax 各种资源的下载消息通知 浏览器原生事件驱动 UI 事件驱动 网络事件驱动 05/09/11
  • 58. 消息模型的优势 编程模型简化 多线程框架由底层实现和托管,我们只负责调用 API 启动线程,发送消息和侦听消息 对编程人员隔离多线程的烦恼 程序耦合性低 程序代码属于被 trigger 的状况,不耦合于目标代码 我讨厌异步特有的 callback 编程,但是我喜欢消息模型 原生的 AOP 编程 05/09/11
  • 59. 消息模型的弱势 无法手动构造多线程实现并行和异步 严重依赖应用的支持 以 web worker 和 websocket 为例,只有浏览器支持的情况下,才能享受到消息模型的好处 事件驱动依赖应用实现 可以手动触发自定义事件,但是对于特殊事件无法触发 05/09/11
  • 60. NodeJS 是一个跑在 Server 端的浏览器 浏览器架构 (Chrome) NodeJS 架构 05/09/11
  • 61. 未来世界也许会是这样的 它也许是浏览器 它也许是 NodeJS 它也许什么应用都不是,但是应用都跑在这上面 05/09/11
  • 62. 为何 JScript 在服务端会死掉? var objConn = Server.CreateObject(&quot;ADODB.Connection&quot;); // open our ADO connection and Execute the SQL command objConn.Open(&quot;dsn=helpdesk&quot;, &quot;sa&quot;, &quot;&quot;); objConn.Execute(strCommandText); var returnErrCode = objConn.Execute(&quot;SELECT @@ERROR as errorCode&quot;).GetString(); objConn.Close(); objConn = null; 05/09/11
  • 63. JScript Vs. NodeJS JScript 之死 摒弃了 JavaScript 在浏览器中的异步机制 完全无视消息模型 无多线程支持 在 Server 端,表现与 PHP 和 ASP 无异 NodeJS 之生 延续 JavaScript 在浏览器端赖以生存的特性 异步,消息模型,事件驱动 向程序员屏蔽多线程 05/09/11
  • 64. 结语 Block UI 的 JavaScript 不是好 JavaScript 无消息模型,不 JavaScript 无事件驱动,不 JavaScript 浏览器是多线程的, JavaScript 是单线程 上一条适用于 NodeJS 05/09/11