资讯专栏INFORMATION COLUMN

拒绝js阻塞—defer、async作用和区别分析

wuaiqiu / 1960人阅读

摘要:阻塞原理浏览器内核可以分成两部分渲染引擎或者和引擎。等引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续和的构建。执行时,解析暂停。从加载完成立即执行来看,模式执行顺序与写的顺序无关,不保证执行顺序。

js阻塞原理

浏览器内核可以分成两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎。早期渲染引擎和 JS 引擎并没有十分明确的区分,但随着 JS 引擎越来越独立,内核也成了渲染引擎的代称(下文我们将沿用这种叫法)。渲染引擎又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件。

JS 引擎是独立于渲染引擎存在的。我们的 JS 代码在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。 因此与其说是 JS 把 CSS 和 HTML 阻塞了,不如说是 JS 引擎抢走了渲染引擎的控制权。

渲染引擎碰到js就交出大权是因为他不知道js的内容会不会对接下来的渲染有没有影响。但是我们引入js的时候是知道有没有影响的,可以根据具体情况用三种方式之一加载js。

JS的三种加载方式

js 有三种加载方式。

正常模式

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

async模式

有 async,script.js会被异步加载,即加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步)。当 script.js加载完整立即执行script.js。执行script.js时,html解析暂停。
从加载完成立即执行来看,async模式 执行顺序与写的顺序无关,不保证执行顺序。

defer 模式

 

有 defer,script.js会被异步加载,即加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步)。这一点与async模式一致。
不同的是当 script.js加载完成并不会立即执行,而是在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。因此它会按照写的顺序执行。

三种方式的直观对比

一图胜千言: 原图地址

来个demo
// html 



    
    
    
    defer-async

    
    
    


    
warp

然后 async1.js 文件巨大(到底有多大,我是把jquery的压缩版拷进来了),然后最后加上 console.log("async1");
文件async2.jsnormal.js 中分别是 console.log("async2");console.log("normal");
打开网页控制台显示如下: async2 先加载完成就先执行了。

而当我把 前面引用换成defer时

    
    
    

同理,defer1.js 里放了jquery的压缩版源码。defer2.js里只放了一句日志; 刷新网页看下日志:

defer1 、defer2还是按照顺序执行的。

把async、defer都加上,

    
    

    
    
    

日志如下:

这个顺序应该不是固定的,符合normal最早,defer1会在 defer2之前的规矩。 至于async 和 defer的前后则要看本身js的加载以及dom树的构建时机吧。

三种方式适合什么时候用

growingwiththeweb 推荐优先级依次是 async defer normal。。

当你的js是个独立的模块且不依赖任何js,使用 async;

如果你的js依赖其他js或者被其他js 依赖,使用 defer;

如果你对js文件很小且被 async script 依赖,使用正常模式的script且放在async script 前面。

可能的坑

虽然理论上defer按加载顺序执行,但也有同学反映事实上并不是这样。。比如这位同学的问题:

我认为这是涉及到 event loop的 task和微任务了。
"在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在 DOMContentLoaded 事件触发前执行,因此最好只包含一个延迟脚本。" 《JavaScript 高级程序设计(第三版)》如是说,所以脚本之间有依赖,最好使用一个异步脚本吧。比如上面同学那个问题 可以改成这样.

参考资料

掘金小册
async vs defer
谈谈script标签
defer async区别

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99709.html

相关文章

  • Sdk加载阻塞页面渲染问题的总结

    摘要:由于始终没有还原阻塞时的情形,无法截图。写模拟场景,由于当时的场景是外链是加载阻塞,而不是执行阻塞,暂时没有有效模拟。但是可以确定的是,使用或可以有效解决,外链阻塞内部执行的问题。 由于始终没有还原阻塞时的情形,无法截图。在正常情况下,是无法区别是否使用defer的区别的。后续看一下是否能模拟场景。写demo模拟场景,由于当时的场景是外链是js加载阻塞,而不是js执行阻塞,暂时没有有效...

    AlphaGooo 评论0 收藏0
  • 浏览器渲染机制

    摘要:修改浏览器渲染因为的阻塞使得解析停止,下载完成之前,页面无法显示任何东西。浏览器渲染解析到文件时出现阻塞。我们把调整到尾部浏览器渲染这是页面可以渲染了,但是没有样式。 本文示例源代码请戳github博客,建议大家动手敲敲代码。 前言 浏览器渲染页面的过程 从耗时的角度,浏览器请求、加载、渲染一个页面,时间花在下面五件事情上: DNS 查询 TCP 连接 HTTP 请求即响应 服务器响...

    FullStackDeveloper 评论0 收藏0
  • 浏览器渲染机制

    摘要:修改浏览器渲染因为的阻塞使得解析停止,下载完成之前,页面无法显示任何东西。浏览器渲染解析到文件时出现阻塞。我们把调整到尾部浏览器渲染这是页面可以渲染了,但是没有样式。 本文示例源代码请戳github博客,建议大家动手敲敲代码。 前言 浏览器渲染页面的过程 从耗时的角度,浏览器请求、加载、渲染一个页面,时间花在下面五件事情上: DNS 查询 TCP 连接 HTTP 请求即响应 服务器响...

    gougoujiang 评论0 收藏0
  • AMD CMD

    摘要:脚本的无阻塞加载代码此处可以放源码使得该文件变大,以便异步加载时看效果代码同步加载输出和在文档完成解析后,触发事件前执行。对动态嵌入的脚本使用来达到类似的效果。是否在允许的情况下异步执行该脚本。该属性对于内联脚本无作用即没有属性的脚本。 脚本的无阻塞加载 moduleA.js 代码 console.log(Im A); /* 此处可以放jquery源码 使得该文件变大,以便异步加载时看...

    王军 评论0 收藏0
  • css、js阻塞

    摘要:例如,当解析器被脚本阻塞时,解析器虽然会停止构建,但仍会识别该脚本后面的资源,并进行预加载。也就是说,会阻塞页面的渲染但是,并不会阻塞的解析。的加载不会阻塞页面的渲染和资源的加载,一旦加载到就会立刻执行。 大家是不是会遇到这样的一个问题,页面加载速度过慢,浏览器老在转圈圈,页面部分内容需要花费较多的时间才能加载出来? 要明白上述问题,我们需要知道是什么在阻塞页面的渲染? 1、浏览器如何...

    gxyz 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<