浅析script标签中的defer与async属性_javascript技巧

一、前言

看到的前辈写的代码如下

<script src="#link("xxxx/xx/home/home.js")" type="text/javascript" async defer></script>

竟然同时有asyncdefer属性,心想着肯定是前辈老司机的什么黑科技,两个一块儿肯定会发生什么神奇化学反应,于是赶紧怀着一颗崇敬的心去翻书翻文档,先复习一下各自的定义。

二、调查一番

先看看asyncdefer各自的定义吧,翻开红宝书望远镜,是这么介绍的

2.1 defer

这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在<script>元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。

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

2.2 async

这个属性与defer类似,都用于改变处理脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。

第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。

概括来讲,就是这两个属性都会使script标签异步加载,然而执行的时机是不一样的。引用segmentfault上的一个回答中的一张图

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

也就是说async是乱序的,而defer是顺序执行,这也就决定了async比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。从图中可以看到一个普通的<script>标签的加载和解析都是同步的,会阻塞DOM的渲染,这也就是我们经常会把<script>写在<body>底部的原因之一,为了防止加载资源而导致的长时间的白屏,另一个原因是js可能会进行DOM操作,所以要在DOM全部渲染完后再执行。

2.3 really?

然而,这张图(几乎是百度搜到的唯一答案)是不严谨的,这只是规范的情况,大多数浏览器在实现的时候会作出优化。

来看看chrome是怎么做的

《WebKit技术内幕》:

      1、当用户输入网页URL的时候,WebKit调用其资源加载器加载该URL对应的网页。

      2、加载器依赖网络模块建立连接,发送请求并接受答复。

      3、WebKit接收到各种网页或者资源的数据,其中某些资源可能是同步或异步获取的。

      4、网页被交给HTML解释器转变成一系列的词语(Token)。

      5、解释器根据词语构建节点(Node),形成DOM树。

      6、如果节点是JavaScript代码的话,调用JavaScript引擎解释并执行。

      7、JavaScript代码可能会修改DOM树的结构。

      8、如果节点需要依赖其他资源,例如图片、CSS、视频等,调用资源加载器来加载他们,但是他们是异步的,不会阻碍当前DOM树的继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。

所以,通俗来讲,chrome浏览器首先会请求HTML文档,然后对其中的各种资源调用相应的资源加载器进行异步网络请求,同时进行DOM渲染,直到遇到<script>标签的时候,主进程才会停止渲染等待此资源加载完毕然后调用V8引擎对js解析,继而继续进行DOM解析。我的理解如果加了async属性就相当于单独开了一个进程去独立加载和执行,而defer是和将<script>放到<body>底部一样的效果。

三、实验一发

3.1 demo

为了验证上面的结论我们来测试一下

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
 <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.css" rel="stylesheet">
 <link href="http://cdn.staticfile.org/foundation/6.0.1/css/foundation.css" rel="stylesheet">
 <script src="http://lib.sinaapp.com/js/angular.js/angular-1.2.19/angular.js"></script>
 <script src="http://libs.baidu.com/backbone/0.9.2/backbone.js"></script>
 <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
</head>
<body>
 ul>li{这是第$个节点}*1000
</body>
</html>

一个简单的demo,从各个CDN上引用了2个CSS3个JS,在body里面创建了1000个li。通过调整外部引用资源的位置和加入相关的属性利用chrome的Timeline进行验证。

3.2 放置在<head>内

异步加载资源,但会阻塞<body>的渲染会出现白屏,按照顺序立即执行脚本

3.3 放置在<body>底部

异步加载资源,等<body>中的内容渲染完毕后且加载完按顺序执行JS

3.3 放置在<head>头部并使用async

异步加载资源,且加载完JS资源立即执行,并不会按顺序,谁快谁先上

3.4 放置在<head>头部并使用defer

异步加载资源,在DOM渲染后之后再按顺序执行JS

3.5 放置在<head>头部并同时使用async和defer

表现和async一致,开了个脑洞,把这两个属性交换一下位置,看会不会有覆盖效果,结果发现是一致的 = =、

综上,在webkit引擎下,建议的方式仍然是把<script>写在<body>底部,如果需要使用百度谷歌分析或者不蒜子等独立库时可以使用async属性,若你的<script>标签必须写在<head>头部内可以使用defer属性

四、 兼容性

那么,揣摩一下前辈的心理,同时写上的原因是什么呢,兼容性?

上caniuse,async在IE<=9时不支持,其他浏览器OK;defer在IE<=9时支持但会有bug,其他浏览器OK;现象在这个issue里有描述,这也就是“望远镜”里建议只有一个defer的原因。所以两个属性都指定是为了在async不支持的时候启用defer,但defer在某些情况下还是有bug。

The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the synchronous blocking behavior that is the default.

五、结论

其实这么讲来,最稳妥的办法还是把<script>写在<body>底部,没有兼容性问题,没有白屏问题,没有执行顺序问题,高枕无忧,不要搞什么deferasync的花啦~

目前只研究了chrome的webkit的渲染机制,Firefox和IE的有待继续研究,图片和CSS以及其他外部资源的渲染有待研究。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索script
, async
, defer
defer和async的区别
script async defer、async defer、defer和async、js defer async、defer async true,以便于您获取更多的相关知识。

时间: 2024-09-20 00:43:04

浅析script标签中的defer与async属性_javascript技巧的相关文章

js的[defer]和[async]属性_javascript技巧

[defer] 可以在<script>中加入defer属性,告诉浏览器这段script不必立即执行,那么浏览器就会在完全载入文档之后再执行这个script,相当于window.onload,但它比window.onload更灵活. 复制代码 代码如下:  <script defer="true"></script> [async] 使用async属性加载JavaScript,这样整个脚本就可以异步加载和执行. <script>标签的def

javascript 获取url参数和script标签中获取url参数函数代码_javascript技巧

url paramter: 复制代码 代码如下: //lastest: var getArgs=function() {//get url querystring var params=document.location.search,reg=/(?:^\?|&)(.*?)=(.*?)(?=&|$)/g,temp,args={}; while((temp=reg.exec(params))!=null) args[temp[1]]=decodeURIComponent(temp[2]);

Script标签与访问HTML页面详解_javascript技巧

复制代码 代码如下: <img src="1_ender1000.jpg" id="img2" alt="" onclick="alert('onclick');" onmouseover="this.src='1_yylklshmyt20090217.jpg'" title="反转图片" />    <script type="text/javascript

浏览器环境下JavaScript脚本加载与执行探析之defer与async特性_javascript技巧

defer和async特性相信是很多JavaScript开发者"熟悉而又不熟悉"的两个特性,从字面上来看,二者的功能很好理解,分别是"延迟脚本"和"异步脚本"的作用.然而,以defer为例,一些细节问题可能开发者却并不一定熟悉,比如:有了defer特性的脚本会延迟到什么时候执行:内部脚本和外部脚本是不是都能够支持defer:defer后的脚本除了会延迟执行之外,还有哪些特殊的地方等等.本文结合已有的一些文章以及MDN文档中对两个特性的阐述,对de

JS中script标签defer和async属性的区别详解_javascript技巧

向html页面中插入javascript代码的主要方法就是通过script标签.其中包括两种形式,第一种直接在script标签之间插入js代码,第二种即是通过src属性引入外部js文件.由于解释器在解析执行js代码期间会阻塞页面其余部分的渲染,对于存在大量js代码的页面来说会导致浏览器出现长时间的空白和延迟,为了避免这个问题,建议把全部的js引用放在</body>标签之前. script标签存在两个属性,defer和async,因此script标签的使用分为三种情况: 1.<script

JavaScript跨浏览器获取页面中相同class节点的方法_javascript技巧

网页开发时,在很多时候我们需要操作相同类名的元素,即class相同的元素.昨天参加笔试,有一道相关的题目没答上来: JavaScript获取页面中class为test的节点 于是收集了一些相关的资料,在本文中列举了两种我觉得比较好的方法,不足之处,还望大家批评指正.如果大家有更好的方法,希望可以分享. Solution1 Jeremy Keuth方案 Jeremy Keuth大叔在<JavaScript DOM 编程艺术>(第2版)(英文:DOM Scripting-Web Design wi

浅析JS中document对象的一些重要属性_javascript技巧

可以通过这些属性访问到页面中的每一个元素和它们的属性,每一个标签都可以通过它们对应的数组去访问他们的属性 一: forms数组对象:代码网页中所有form标签的集合 二: anchors数组对象:指定了网页中所有指定了name或id属性的<a>标签的集合,但不包括指定了href标签的集合 三:links数组对象  指所有指定了href属性的<a>标签的集合 四:images数组对象:指定了文档中所有的<img>标签的集合 五:scripts数组对象:代表了文档中所有的&

浅析网站优化中站内文章作用和编辑技巧

中介交易 SEO诊断 淘宝客 云主机 技术大厅 网站优化中分为站内优化和站外优化,其中站内优化包括网站结构设计,网站域名空间选择等等,在这些准备好后就可以经营一个网站,排除这些,网站的经营无非就是往网站上面添加内容,写文章,那么在网站上写文章有什么技巧呢,它和我们在自己的博客QQ空间上写文章一样吗,对于网络编辑人员需要了解网站优化中站内文章作用还有编辑技巧,这样才会更大发挥出网络编辑人员的自身价值和网站流量最大化.现在开始分段讲解: 一:文章在网站优化中的作用 定时更新文章可以增加网站文章收录,

再谈Javascript中的异步以及如何异步_javascript技巧

为什么需要异步?why?来看一段代码. 问题1: for(var i=0;i<100000;i++){ } alert('hello world!!!'); 这段代码的意思是执行100...次后再执行alert,这样带来的问题是,严重堵塞了后面代码的执行,至于为什么,主要是因为JS是单线程的. 问题2: 我们通常要解决这样一个问题,如果我们需要在head里面加入script代码的话,一般会将代码写在window.onload里面(如果操作了dom的话),你有没有想过,为什么要加window.on