• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
 

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

on

  • 3,838 views

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

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

Statistics

Views

Total Views
3,838
Views on SlideShare
3,834
Embed Views
4

Actions

Likes
17
Downloads
114
Comments
2

2 Embeds 4

http://www.slideshare.net 2
http://www.iu818.com 2

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • dynamic load js with no blocking
    Are you sure you want to
    Your message goes here
    Processing…
  • 从无阻塞并行脚本加载(Lab.js)到浏览器消息模型
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • 从无阻塞脚本加载 (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