摘要:也就是说是乱序的,而是顺序执行,这也就决定了比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。然而,这张图几乎是百度搜到的唯一答案是不严谨的,这只是规范的情况,大多数浏览器在实现的时候会作出优化。
1. 什么鬼
今天在做一个小需的时候,忽然看到前辈一句吊炸天的代码
卧槽,竟然同时有async和defer属性,心想着肯定是前辈老司机的什么黑科技,两个一块儿肯定会发生什么神奇化学反应,于是赶紧怀着一颗崇敬的心去翻书翻文档,先复习一下各自的定义。
2. 调查一番先看看async和defer各自的定义吧,翻开红宝书望远镜,是这么介绍的
2.1 defer2.2 async这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。
HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad时间触发前执行,因此最好只包含一个延迟脚本。
这个属性与defer类似,都用于改变处理脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。
第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。
概括来讲,就是这两个属性都会使script标签异步加载,然而执行的时机是不一样的。引用segmentfault上的一个回答中的一张图蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。
也就是说async是乱序的,而defer是顺序执行,这也就决定了async比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。从图中可以看到一个普通的标签的加载和解析都是同步的,会阻塞DOM的渲染,这也就是我们经常会把写在底部的原因之一,为了防止加载资源而导致的长时间的白屏,另一个原因是js可能会进行DOM操作,所以要在DOM全部渲染完后再执行。
2.3 really?然而,这张图(几乎是百度搜到的唯一答案)是不严谨的,这只是规范的情况,大多数浏览器在实现的时候会作出优化。
来看看chrome是怎么做的
《WebKit技术内幕》:
当用户输入网页URL的时候,WebKit调用其资源加载器加载该URL对应的网页。
加载器依赖网络模块建立连接,发送请求并接受答复。
WebKit接收到各种网页或者资源的数据,其中某些资源可能是同步或异步获取的。
网页被交给HTML解释器转变成一系列的词语(Token)。
解释器根据词语构建节点(Node),形成DOM树。
如果节点是JavaScript代码的话,调用JavaScript引擎解释并执行。
JavaScript代码可能会修改DOM树的结构。
如果节点需要依赖其他资源,例如图片、CSS、视频等,调用资源加载器来加载他们,但是他们是异步的,不会阻碍当前DOM树的继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。
所以,通俗来讲,chrome浏览器首先会请求HTML文档,然后对其中的各种资源调用相应的资源加载器进行异步网络请求,同时进行DOM渲染,直到遇到标签的时候,主进程才会停止渲染等待此资源加载完毕然后调用V8引擎对js解析,继而继续进行DOM解析。我的理解如果加了async属性就相当于多带带开了一个进程去独立加载和执行,而defer是和将放到底部一样的效果。
3. 实验一发 3.1 demo为了验证上面的结论我们来测试一下
Document ul>li{这是第$个节点}*1000
一个简单的demo,从各个CDN上引用了2个CSS3个JS,在body里面创建了1000个li。通过调整外部引用资源的位置和加入相关的属性利用chrome的Timeline进行验证。
3.2 放置在内
异步加载资源,但会阻塞的渲染会出现白屏,按照顺序立即执行脚本
异步加载资源,等中的内容渲染完毕后且加载完按顺序执行JS
异步加载资源,且加载完JS资源立即执行,并不会按顺序,谁快谁先上
异步加载资源,在DOM渲染后之后再按顺序执行JS
表现和async一致,开了个脑洞,把这两个属性交换一下位置,看会不会有覆盖效果,结果发现是一致的 = =、
综上,在webkit引擎下,建议的方式仍然是把写在底部,如果需要使用百度谷歌分析或者不蒜子等独立库时可以使用async属性,若你的标签必须写在头部内可以使用defer属性
4. 兼容性那么,揣摩一下前辈的心理,同时写上的原因是什么呢,兼容性?
上caniuse,async在IE<=9时不支持,其他浏览器OK;defer在IE<=9时支持但会有bug,其他浏览器OK;现象在这个issue里有描述,这也就是“望远镜”里建议只有一个defer的原因。所以两个属性都指定是为了在async不支持的时候启用defer,但defer在某些情况下还是有bug。
5. 结论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.
其实这么讲来,最稳妥的办法还是把写在底部,没有兼容性问题,没有白屏问题,没有执行顺序问题,高枕无忧,不要搞什么defer和async的花啦~
目前只研究了chrome的webkit的渲染机制,Firefox和IE的有待继续研究,图片和CSS以及其他外部资源的渲染有待研究。
更多信息在 这里
参考JavaScript高级程序设计
WebKit技术内幕
defer和async的区别
www.w3.org
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80262.html
摘要:相关脚本会立即下载并执行。从上面两个例子,可以充分了解到标签的柱塞式执行。表示该标签并不柱塞,也不同步执行。属性带有属性的脚本,同样会推迟脚本的执行,并且不会阻止文档解析。同时,带有的脚本彼此之间,能保证其执行顺序。 原文: http://pij.robinqu.me/Browser_Scripting/Document_Loading/ScriptTag.html 源...
摘要:阻塞原理浏览器内核可以分成两部分渲染引擎或者和引擎。等引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续和的构建。执行时,解析暂停。从加载完成立即执行来看,模式执行顺序与写的顺序无关,不保证执行顺序。 js阻塞原理 浏览器内核可以分成两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎。早期渲染引擎和 JS 引擎并没有十分明确的区分,但随...
摘要:紧接着发现,于是又停了,浏览器下载并执行完,继续。,发现,遂将中文字展示了出来。的执行时间是在所有元素解析完成之后,事件触发之前。的执行时间是在当前脚本下载完成后,所以多个是执行顺序是不固定的。至此,完美的结构出炉了。 现代浏览器性能优化-JS篇 众所周知,JS的加载和执行会阻塞浏览器渲染,所以目前业界普遍推荐把script放到之前,以解决js执行时找不到dom等问题。但随着现代浏览器...
摘要:紧接着发现,于是又停了,浏览器下载并执行完,继续。,发现,遂将中文字展示了出来。的执行时间是在所有元素解析完成之后,事件触发之前。的执行时间是在当前脚本下载完成后,所以多个是执行顺序是不固定的。至此,完美的结构出炉了。 现代浏览器性能优化-JS篇 众所周知,JS的加载和执行会阻塞浏览器渲染,所以目前业界普遍推荐把script放到之前,以解决js执行时找不到dom等问题。但随着现代浏览器...
摘要:尽管脚本的下载过程中不会相互影响,但页面仍然要等到所有代码下载并完成执行才能继续。 defer和asnyc(只对外部文件有效) defer 在页面完成解析时执行代码,这个属性表明脚本在执行时不会影响页面的构造,在元素中设置这个属性相当于告诉浏览器立即下载但延迟执行 async 相对于页面其他部分异步执行脚本,一般的script标签都是会阻塞页面执行的,没有加上async属性的标签...
阅读 2104·2023-04-25 14:56
阅读 2381·2021-11-16 11:44
阅读 2653·2021-09-22 15:00
阅读 1878·2019-08-29 16:55
阅读 2118·2019-08-29 14:04
阅读 2287·2019-08-29 11:23
阅读 3644·2019-08-26 10:46
阅读 1886·2019-08-22 18:43