最爱牛腩的小牛-小灰灰

又一个 WordPress 站点

script的defer和async

我们常用的script标签,有两个和性能、js文件下载执行相关的属性:defer和async

defer的含义【摘自https://developer.mozilla.org/En/HTML/Element/Script

This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed.

async的含义【摘自https://developer.mozilla.org/En/HTML/Element/Script

Set this Boolean attribute to indicate that the browser should, if possible, execute the script asynchronously.

Defer

对于defer,我们可以先思考一个情况。一个页面如果有N个外链的脚本,放在head中,那么,当加载脚本时会阻塞页面的渲染,也就是常说的空白。在简单的开发环境中,我们可能只要将源代码中的外链脚本位置换一下就ok了。可是面对越来越复杂的开发环境,前端同事如果要后台开发同事调整一下脚本的位置,可能会花费大量的沟通成本和开发成本。我在去年的一个项目中就遇到过此类情况,当然也很感谢当时的后台开发同事的配合,他们都辛辛苦苦的调整了脚本的位置,解决了空白的问题。

那么可以让这个成本降到最低吗?那么我们可以使用defer这个属性。

如果一个script加了defer属性,即使放在head里面,它也会在html页面解析完毕之后再去执行,也就是类似于把这个script放在了页面底部。

关于defer有两个demo:

简单介绍一下这个demo,一共引用了3个js和1个css,为了能更好的展示defer的效果,第二个js-2.php是延迟了3秒返回的。1.js会在页面中生成一个值为1的input框,2.php会生成值为2的input框,3.js会生成值为3的input框。一方面我们需要观察页面渲染的时间,另一方面我们也要看一下js是否顺序执行了。

下图是without_defer.html的效果,从瀑布图可以看出,domready和onload的时间都在6s左右,因为需要等待2.php的返回才能渲染页面。如果你访问上面的例子,可以看出,页面要等6s的时间才会呈现出来,6s之前都是空白。

那么如果我们为每个js都加上defer属性,请看下面两张图

第一张是在加载过程中截取的,可以看到一旦有了defer属性,虽然有资源2.php需要等待,但是仍然会继续渲染页面,加载后续的js和css等资源文件。对比上面的情况,可以看到domready的时间明显提前,如果你访问demo地址,会发现页面会照常渲染出来,只不过2.php里面的内容会延迟执行。

从上面的对比可以看出,对于defer,我们可以认为是将外链的js放在了页面底部。js的加载不会阻塞页面的渲染和资源的加载。不过defer会按照原本的js的顺序执行,所以如果前后有依赖关系的js可以放心使用。

Async

对于async,这个是html5中新增的属性,它的作用是能够异步的加载和执行脚本,不因为加载脚本而阻塞页面的加载。一旦加载到就会立刻执行。那async和defer有什么不同之处呢?我们还是先看async的两个demo

demo的效果和上面描述的一样。

下图是without async的瀑布图,和没有defer的情况是一样的。domready和load的时间都因为一个js的延迟而延迟了。
without_async

我们再看一下有async属性的情况,和defer一样,会等待的资源不会阻塞其余资源的加载,也不会影响页面的加载。但是有一点需要注意下,在有async的情况下,js一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果js前后有依赖性,用async,就很有可能出错。

with_async

Difference

这篇文章中总结了defer和async的相同点和区别。

Both async and defer scripts begin to download immediately without pausing the parser and both support an optional onload handler to address the common need to perform initialization which depends on the script. The difference between async and defer centers around when the script is executed. Each async script executes at the first opportunity after it is finished downloading and before the window’s load event. This means it’s possible (and likely) that async scripts are not executed in the order in which they occur in the page. The defer scripts, on the other hand, are guaranteed to be executed in the order they occur in the page. That execution starts after parsing is completely finished, but before the document’s DOMContentLoaded event.

Wrapping it up

在上述的基础上,我根据实际使用的情况总结了一下defer和async的特征。
相同点:

  • 加载文件时不阻塞页面渲染
  • 对于inline的script无效
  • 使用这两个属性的脚本中不能调用document.write方法
  • 有脚本的onload的事件回调

区别点:

  • html的版本

    html4.0中定义了defer;html5.0中定义了async

  • 浏览器
    Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
    Basic support 1.0 1.0 (1.7 or earlier) (Supported) (Supported) (Supported)
    async attribute (Supported) 3.6 (1.9.2) 10 (Supported)
    defer attribute (Supported) 3.5 (1.9.1) 4 (Supported)
  • 执行时刻
    每一个async属性的脚本都在它下载结束之后立刻执行,同时会在window的load事件之前执行。所以就有可能出现脚本执行顺序被打乱的情况;每一个defer属性的脚本都是在页面解析完毕之后,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行。

摘自【http://dev.w3.org/html5/spec/Overview.html#attr-script-async

There are three possible modes that can be selected using these attributes. If the async attribute is present, then the script will be executed asynchronously, as soon as it is available. If the async attribute is not present but the defer attribute is present, then the script is executed when the page has finished parsing. If neither attribute is present, then the script is fetched and executed immediately, before the user agent continues parsing the page.

简单的来说,使用这两个属性会有三种可能的情况

  • 如果async为true,那么脚本在下载完成后异步执行。
  • 如果async为false,defer为true,那么脚本会在页面解析完毕之后执行。
  • 如果async和defer都为false,那么脚本会在页面解析中,停止页面解析,立刻下载并且执行,

最后给一点个人的建议,无论使用defer还是async属性,都需要首先将页面中的js文件进行整理,哪些文件之间有依赖性,哪些文件可以延迟加载等等,做好js代码的合并和拆分,然后再根据页面需要使用这两个属性。

Posted in firefox, javascript | No Comments

浅谈购物团队游营销模式

2012的元旦期间,去了趟三亚旅行,第一次看到了湛蓝的海水,梦中的海天一色。光着脚丫,踏着柔软的细沙,海浪时不时淹过脚踝,那种感觉真的~很惬意~很温馨~

三亚的朋友很热情,给我们预订了两天的XX旅行社的团队游。

第一天:南山寺和天涯海角;第二天:博鳌和植物园。

more>

Posted in 杂谈 | No Comments

ControlJS part 3: overriding document.write

原文:http://www.stevesouders.com/blog/2010/12/15/controljs-part-3/

关于ControlJs一共有三篇文章,这是第二部分。ControlJS是让脚本加载更快的一个模块(a javascript module for making scripts load faster). 三篇文章的结构分别为:

1. async loading [中文翻译:http://ued.ctrip.com/blog/?p=2964]
2. delayed execution
[中文翻译:http://ued.ctrip.com/blog/?p=3017]

3. overriding document.write [中文翻译:http://ued.ctrip.com/blog/?p=3111]

ControlJS的目标是让开发者更好的控制脚本的加载。其中的关键是意识到”loading”有两个步骤:下载[download](获取内容)和执行[execution](包括解析).在ControlJS part 1(中文翻译:http://ued.ctrip.com/blog/?p=2964)中,我主要阐述了如何用ControlJS来异步加载脚本。在 ControlJS part 2 (中文翻译:http://ued.ctrip.com/blog/?p=3017)中,我们展示了通过延缓脚本的执行使页面更快速的加载,尤其是Ajax的网络应用,下载了大量的脚本,而这些脚本不需要立刻使用到的。在本篇中,我们将讨论一下在脚本异步加载时,document.write引发的问题。

more>

Posted in javascript | No Comments

ControlJS part 2: delayed execution

原文:http://www.stevesouders.com/blog/2010/12/15/controljs-part-2/

关于ControlJs一共有三篇文章,这是第二部分。ControlJS是让脚本加载更快的一个模块(a javascript module for making scripts load faster). 三篇文章的结构分别为:

1. async loading [中文翻译:http://ued.ctrip.com/blog/?p=2964]
2. delayed execution
[中文翻译:http://ued.ctrip.com/blog/?p=3017]

3. overriding document.write [中文翻译:http://ued.ctrip.com/blog/?p=3111]

ControlJS的目标是让开发者更好的控制脚本的加载。其中的关键是意识到”loading”有两个步骤:下载[download](获取内容)和执行[execution](包括解析).在ControlJS part 1(中文翻译:http://ued.ctrip.com/blog/?p=2964)中,我主要阐述了如何用ControlJS来异步加载脚本。在本篇中,我将讲一讲ControlJS在脚本执行阶段[execution]能做些什么?

The issue with execution

执行阶段的问题关键是:浏览器在解析和执行脚本的时候,它其他什么都干不了。通常的表现就是浏览器的界面无响应,页面渲染被阻止,浏览器中新资源的下载被终止。

more>

Posted in javascript | No Comments

ControlJS part 1: async loading

原文:http://www.stevesouders.com/blog/2010/12/15/controljs-part-1/

关于ControlJs一共有三篇文章,这是第一部分。ControlJS是让脚本加载更快的一个模块(a javascript module for making scripts load faster). 三篇文章的结构分别为:

1. async loading [中文翻译:http://ued.ctrip.com/blog/?p=2964]
2. delayed execution
[中文翻译:http://ued.ctrip.com/blog/?p=3017]

3. overriding document.write [中文翻译:http://ued.ctrip.com/blog/?p=3111]

关 于第一部分的异步加载,这个的关键在于尽快将页面作为html绘制出来,然后再用javascript进行优化,或者说用js进一步渲染。我们看到过很多 网页,展示给用户一片空白,只是因为在等待几百KB的js文件下载、解析、执行,而这些js是用来绘制页面中展示给用户看的DOM元素。

more>

Posted in javascript | No Comments

JavaScript. The core.

最近读到了一篇JavaScript的文章,觉得不错。原本的中文翻译又404,所以我开始边读边翻译。这篇主要就是介绍js里面的一些非常基本但是又很重要的概念。

原文地址:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

翻译备注:因为有些英文翻译成中文会更加难懂,所以一些术语或者必要的地方,都是中文在前,后面紧随中括号中的英文。希望能让大家更好的读懂。

全文分为10个主要部分

本篇是ECMA-262-3 in detail系列的概述。每个章节都有一个更详细的内容链接,如果你觉得某个章节解释的不够过瘾,可以继续读一下每个章节对应的详细内容链接。

适合的读者:有经验的开发员,专业前端人员。

more>

Posted in javascript | No Comments

Firefox性能检测和应用 III

一直以为检查JS语法错误非jslint不可,不过使用起来总是觉得太重量级了一点点。

后来无意中发现了一个叫jshint的东东。

首先介绍一下,jshint和jslint的差别在哪里。

摘自官网的一段内容

JSHint is a fork of Douglas Crockford’s JSLint that is designed to be more flexible than the original. Our goal is to make a tool that helps you to find errors in your JavaScript code and to enforce your favorite coding style.

We realize that people use different styles and conventions, and we want our tool to adjust to them. JSHint will never enforce one particular convention.

大概的意思就是,JSHint比起JSLint而言,会更加轻量级一些。它能够找出代码中的语法错误,并且建议更好的一种编码风格。当然,它也不是强制性的非要你根据它规定的编码风格来做。因为它提供了一系列的配置,你可以随时关掉某些你觉得不必要的错误提示。这个我后面会介绍到。

那么如何使用jsHint检查错误呢?用起来非常简单哦~

var result = JSHINT(source, options);

more>

Posted in firefox, javascript | No Comments

Firefox性能检测和应用 II

本篇主要介绍了一下performance Timing的一系列指标项,然后根据这些指标项如何检测页面的domready,load等消耗的时间。

参考文档:
一般来说,如果需要知道页面的domready消耗的时间或者load的时间,我们会需要在页面中的某些地方加入new Date()的代码来实现.例如下方:

  1. <html>
  2. <head>
  3. </head>
  4. <script type=“text/javascript”>
  5. var t1 = new Date();
  6. </script>
  7. <body>
  8. </body>
  9. <script type=“text/javascript”>
  10. $r(“domready”, function(){
  11. var t2 = new Date();
  12. });
  13. </script>
  14. </html>

这种方法最要命的是,我们要在页面中加上N个时间点,不改动页面就获取不到domready的时间。

more>

Posted in firefox, javascript | No Comments

Firefox性能检测和应用 I

最近在做一些性能方面的工作,开始研究一下firebug针对网页性能的强大功能。

今天主要介绍一下console.timeStamp

自从Firebug 1.8b3开始, 引入了一个新的API-console.timeStamp(). 这个API在javascript执行过程中设置时间点,并且在“网络”面板中,和HTTP的请求信息一起显示出来,进行比对。
more>

Posted in javascript | No Comments