html5中defer的属性,JavaScript中defer和async属性相关测试

测试的缘由

很早就接触了script标签中的defer和async这两个属性,当时也仅限知道他们的作用是起到延迟脚本而达到避免阻塞脚本的作用。当时并未对这两个属性进行深入的研究,直到我学习seajs和RequireJS这两个模块加载器中或多或少涉及了这两标签的使用。seajs通过异步的ajax请求脚本并执行,而RequireJS则是将依赖的脚本通过 head.appendchild(script)并设置延迟属性来达到异步加载脚本的目的。

不行请看:

1472749780_7329.png

而且对它的网络瀑布流进行分析时发现一个奇怪的现象,有时强制刷新时,带async的脚本会延迟到window的onload事件执行完毕后才执行。这样就会很容易导致一个问题,在这些脚本中绑定window的onload事件是失败的。因为window的onlaod执行先于这些延迟的脚本。如图所示:

1472749780_2409.png

图中的蓝线表示DOM执行完毕的时间轴,而红线表示window的load执行完毕的时间轴。你会发现那些通过RequireJS加载的模块脚本会在load事件执行完毕后执行。而且更有意思的是,这种情况也是随机出现的。

这完全颠覆了我对defer和async的理解,所以有必要对其进行测试。

defer和async定义

在进行测试前有必要介绍标准对其进行的定义,同时进行兼容性测试。首先来看看标准的定义吧。

defer

This Boolean attribute is set to indicate to a browser that the script

is meant to be executed after the document has been parsed. Since this

feature hasn’t yet been implemented by all other major browsers,

authors should not assume that the script’s execution will actually be

deferred. The defer attribute shouldn’t be used on scripts that don’t

have the src attribute. Since Gecko 1.9.2, the defer attribute is

async

Set this Boolean attribute to indicate that the browser should, if possible, >execute the script asynchronously. It has no effect on inline scripts (i.e., >scripts that don’t have the src attribute).

ignored on scripts that don’t have the src attribute. However, in

Gecko 1.9.1 even inline scripts are deferred if the defer attribute is

set.

英文很渣,看不懂怎么办,没事,看看《JavaScript高级程序设计3》(第二章第13页)是怎么解释的。

defer

HTML4.01为

在HTML5规范中要求脚本按照它们的出现的先后顺序执行,而且脚本会优先于DOMContentLoaded事件执行。在现实当中,延迟的脚本并不一定按照顺序执行,也不一定会在DOMContentLoaded事件触发执行,因此最好只包含一个延迟脚本。

async

HMTL5为

异步脚本一定会在页面的load事件前执行,但可能会在DOMContentLoad事件触发之前或之后执行。

兼容性

官方给出的兼容性测试

1472749782_8929.png

不过,你懂得,浏览器更新迭代太快了,这个版本支持,下个版本可能就不支持或者支持的不够完整,为了测试我机器上浏览器上的支持情况,你可以引入检查的脚本,链接为:http://segmentfault.com/blog/liangyi/1190000002434713#articleHeader3

通过运行得到本机所安装浏览器支持的情况:

IE6、IE7、IE8、IE9、Chrome39、FF34都支持defer属性。

IE6、IE7、IE8、IE9不支持async属性,但是Chrome39、FF34支持。

测试环境部署

在得到兼容性的报表后,我们就可以进行测试了,但是首先还得交代测试用的Demo示例。本示例用到两个测试文件,代码分别如下:

demo.html

script async defer

$(window).on("load", function (e) {

console.log("WindowLoaded");

});

$(document).ready(function (e) {

console.log('DOMContentLoaded');

});

测试延迟和异步加载脚本

console.log("Inline script Loaded");

server.js

var http = require('http');

http.createServer(function(req, res) {

res.writeHead(200, {

'Content-Type': 'text/plain'

});

if (req.url == '/test1') {

setTimeout(function() {

res.end("console.log('test1');");

}, 1000 * 5);

} else if (req.url == '/test2') {

setTimeout(function() {

res.end("console.log('test2');");

}, 1000 * 3);

} else {

res.end("console.log('test3');");

}

// res.end('Hello World/n');

}).listen(8081, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8081/');

这里用Node做服务器。请求的连接根据URL不同来请求不同的脚本资源。你也可以用其他的服务器,比Apache、IIS等。或着更直接,将请求的三个脚本资源放在本地。不过相比Node是最简单的部署方式,而且我使用WebStorm可以直接运行和调式Node脚本。

PS:小技巧:如果你使用WebStorm,可以直接点击右上角的浏览器来运行demo.html,它会启动内置的浏览器来运行页面,这样模拟更真实。

测试用列

不包含defer和async的基准测试,demo.html的代码如下所示:

控制台的输出情况(IE6-9,FF34、Chrome39):

test1 -> test2 -> test3-> ->Inline script Loaded -> DOMContentLoaded -> WindowLoaded

总结:这输出的结果和我预想的结果完全吻合,因为在不加任务延迟或者异步的情况下,浏览器遇到Script标签就会下载并执行,而且执行的顺序是同步的,所以尽管test3下载最快,但是会在最后执行。另外加载和执行这些脚本会阻塞页面的渲染,所以会白屏一段时间,而且Inline script Loaded的输出是后于test3输出的。

只包含defer,不包含async的测试,demo.html的改动如下:

在IE6-8输出的情况如下所示:

Inline script Loaded -> DOMContentLoaded -> test1 -> test2 -> test3 -> WindowLoaded

而在IE9、Chrome39、FF34中输出的情况如下所示:

Inline script Loaded -> test1 -> test2 -> test3-> DOMContentLoaded -> WindowLoaded

总结:跟上面的结果做对比,你会发现页面中标题的文本一下就渲染出来了,而且body元素中的script标签脚本是由于其他脚本执行结束的。虽然并不是所有的浏览器中的带defer的脚本等DOMContentLoaded结束后才执行,但是它们都是在body中的元素渲染完毕后才执行的。也就是说defer确实做到了没有阻塞页面的渲染。

只包含async,不包含defer的测试,demo.html的改动如下:

由于我的机器上只有FF和Chrome支持async,所以只对上述的浏览器做了测试,测试结果如下所示:

chrome控制台输出的结果为:

Inline script Loaded -> DOMContentLoaded -> test3 -> test2 -> test1

WindowLoaded

FF控制台输出的结果为:

Inline script Loaded -> test3 -> DOMContentLoaded -> -> test2 -> test1

WindowLoaded

总结:首先他们都没有阻塞页面的渲染,在请求的一瞬间,页面上的标题文本,立即渲染出来了,通过Inline script Loaded最早被输出我们也可以知道。带async属性的外链脚本,一下载完成,就被执行了,所以你看到的顺序是test3 > test2 > test1。他们的顺序不管在哪个浏览器中出现的顺序都是一样的。但是为什么在FF中,test3执行的比DOMContentLoaded

早,我就不太明白,这点希望有高人指点。async属性也做到了没有阻塞页面渲染。但是执行的机制却和defer打不相同。

帮助文档

本文,主要是通过测试的目的来验证defer和async的加载机制,更多细节可以参考下面的链接:

script的defer和async – 携程UED

引用JavaScript文件时的两个属性defer和async

async vs defer attributes

defer和async的区别

script关于async与defer属性的测试 – 测试文件参考这位大牛

同时,文章写的有些长处,如果文章有什么出入,希望大神不吝指点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值