【深入理解 script 标签的 async,defer 属性(同步加载,异步加载,延迟执行)】
程序员文章站
2022-04-29 12:56:50
...
async,defer 属性 同步加载,异步加载,延迟执行
1. 异步加载 和 延迟执行 存在的意义 : 避免影响 页面渲染/显示
-
浏览器解析(渲染/显示)的 阻塞问题
- 加载和解析 顺序: 浏览器在 解析 HTML 页面代码时, 是 从上到下 依次 加载, 解析、渲染
-
阻塞 1: 外部脚本 js 文件的 加载和执行, 影响 页面渲染(显示).
- 如果 , 在
<head>
中 ,用<scrip>
引用了一个 外部脚本js
文件<script src="home.js" type="text/javascript"></script>
- 而且这个 文件很大或者有问题,需要 2 秒的加载和执行时间,那么在这个时间中, 浏览器 会停止渲染页面,页面就会是 白屏显示,什么内容 都没有),2 秒后 脚本加载和执行完成 才会继续渲染页面 ,页面才显示.
- 用户体验 差: 这个时候 用户体验就很不好,因为有几秒什么也看不见,急脾气的用户,可能会直接关闭 页面.
- 这种影响 页面解析(渲染,显示)的行为, 就是阻塞。
- 如果 , 在
-
阻塞 2: 外部 css 文件的加载和执行, 影响 页面渲染(显示).
-
CSS
文件 也一样,因为CSS
文件 会对DOM的 样式,布局,色彩等外观 产生影响,所以浏览器会等CSS
文件 下载并执行完成后 继续。
为了页面的性能,要避免阻塞。
-
2. 同步 加载和执行 (影响 页面解析/渲染/显示)
-
默认 同步加载: 脚本
<script>
标签 默认用的都是 同步加载。<script src="imagescript.js"></script>
-
同步模式
- 又称 阻塞 模式,会阻止 浏览器的后续处理,停止了 后续的文件的 解析
- ① 立即加载: 如果
async,defer
两个属性 都不存在,则立即加载(获取) 脚本 - ② 立即执行: 加载完成后, 立刻执行 脚本, 直到 脚本加载和执行 完成,才能进行 页面的解析/渲染/显示。
- 这个时候,加载和执行 脚本, 都会 阻塞(打断)解析,多多少少 都会影响页面的显示速度.
- ① 立即加载: 如果
- 又称 阻塞 模式,会阻止 浏览器的后续处理,停止了 后续的文件的 解析
-
使用 同步加载和执行 的时机:
- 浏览器 需要采用 同步模式,一般因为 加载的
js
文件中 有对dom
的操作,如, 重定向 等 默认行为,所以同步才是 最安全的。
- 浏览器 需要采用 同步模式,一般因为 加载的
-
把
<script>
标签 放在</body>
前的意义:- 一般来说,
<script>
都是放在<head>
中,来引用 外部脚本 js 文件的 - 也会把要加载的
js
文件<script>
放到</body>
之前,使得js
文件 可 在 页面最后 加载,尽量减少 阻塞页面的渲染/显示。这样可以 先让页面显示出来.
- 一般来说,
-
同步加载的 加载和执行 过程示例图
- ① 加载:不是 并行的,会打断 页面解析
- ② 执行: 不是 并行的, 会打断 页面解析
- 加载和执行,都会 打断页面解析,影响 页面显示.
-
示例1: 下图, 是在 淘宝网的代码中 截的图,可以看到 把很多
<script>
标签 ,放在了 文档底部的位置,在 文档主体 结束标签</body>
之前.
-
示例2: 下图,是在 MDN 网站的截图, 也有
<script>
标签,被放在了 文档的底部,</body>
标签之前.
3. async 属性: 异步加载 (不是 异步执行,搭配 src 属性,仅限 外部脚本)
-
脚本的 异步加载:
- ① 并行加载(获取): 对于脚本,如果有
async
属性,那么脚本 将在 页面解析过程中被 并行获取 - ② 加载/下载完 立即执行: 并在 脚本文件加载(下载)完成 可用时(可能在 页面解析完成 之前) 进行执行。
- ③ 执行 会打断解析: 加载 不打断页面解析,但 执行会打断 页面解析,记住,只是异步加载,执行 并不是异步的,还要回到原来的 流水线上.
-
注意: 搭配
src
事项,异步加载, 仅限 外部脚本
- ① 并行加载(获取): 对于脚本,如果有
-
模块脚本(
module scripts
)的 异步加载- ① 并行获取对于模块脚本,如果有 异步加载
async
属性,那么将在解析过程中 并行地获取 模块脚本及其所有依赖项 - ② 下载完成 立即执行: 并且一旦模块脚本可用(可能在 页面解析完成之前),就会对其进行执行。
- 否则,模块脚本 及其依赖项 将在解析过程中 并行获取,并在页面完成解析后 进行执行。
-
注: 延迟执行
defer
属性 对模块脚本 没有影响。
- ① 并行获取对于模块脚本,如果有 异步加载
-
异步加载
async
的 加载和执行 过程示例图- ① 加载:是并行的,不影响 页面解析
- ② 执行: 不是并行的, 会打断 页面解析
- 加载 不打断页面解析,执行 会打断页面解析.
-
示例1: 在MDN网站的 文档头部标签
<head>
中,也出现了 异步加载的 js文件<script>
4. defer 属性: 延迟执行 (不是 延迟加载,加载是 并行的,搭配 src 属性,仅限 外部脚本 )
-
延迟执行:
defer
- ① 并行获取: 如果 异步加载
async
属性 不存在,而 延迟执行defer
属性 存在,那么脚本将被 并行获取- 加载/获取/下载,都是一个意思
- ② 延迟执行: 并在页面完成解析后 进行执行。
-
注意: 搭配
src
属性,延迟执行, 仅限 外部脚本
- ① 并行获取: 如果 异步加载
-
延迟执行
defer
的 加载和执行 过程示例图- ① 加载:是并行的,不影响 页面解析
- ② 执行: 不是并行的, 但不会打断 页面解析,因为是在 页面解析完成后 才执行的.
- 加载和执行,都不会打断 页面解析.
-
延迟执行的 使用时机
- 延迟执行:
- 有些
js
代码 并不是 页面初始化的时候 就立刻需要的,而在 稍后的某些情况才需要的。 - 那么一开始 并不执行 这些暂时不用的
js
,而是在需要的时候 或稍后 再通过js
的控制来执行,也不会影响。
- 有些
- 延迟执行:
-
根据
js
的需要时机 进行分类:- 将
js
切分成许多模块,页面初始化时 只执行 需要立即执行的js
可以使用 同步加载 ,可以放在head
的script
标签中 - 其它 js ,可以延迟执行。
- 比如, 页面有大量不同的模块 组成,很多可能 暂时不用或根本就没用到。
- 就像图片的延迟加载,在图片出现在可视区域内时(在滚动条下拉)才加载显示图片.
- 将
-
延迟加载: 除了延迟执行,其实也可以设置 延迟加载,就是把 代码片段,写在 文档的底部,
</body>
的前面,这样 文档按顺序加载和解析的时候,到最后 才会加载这些代码片段. -
注意:
defer
只是 延迟执行,并不是 延迟加载,在页面解析的同时,就并行加载了,也不会影响页面解析. -
冲突事项:
- 用了
defer
不要使用document.write()
方法;(为什么 ?因为涉及到 页面显示吗?延迟执行了,会影响页面显示?)
- 用了
-
示例1: MDN 网站中,在 文档的 底部,
</body>
之前,就添加了 延迟执行的 脚本文件.
-
示例2: 延迟加载 (无关
defer
)- 如果 页面 初始的渲染, 并不依赖于
js
或者css
, 可以用 延迟加载 - 即,最后加载
js, css
代码,把 引用外部文件的代码 写在最后</body>
前面。 - 比如,一些按钮的 点击事件,轮播图动画的脚本 也可以放在最后。
- 如果 页面 初始的渲染, 并不依赖于
<html>
<head>
</head>
<body>
<!--延迟加载 -->
<script type="text/javascript" src="a.js"></script>
<link href="a.css" rel="stylesheet" />
</body>
</html>
5. 同步加载,异步加载,延迟执行的 联系和区别
-
⑴ 同步加载,异步加载
async
,延迟执行defer
的 加载 和 执行 示例图- 绿色: 页面解析(渲染/显示)
- 蓝色: 脚本 加载(下载/获取)
- 红色: 脚本执行
-
① 同步加载的 加载和执行 过程
- 加载:不是 并行的,会打断 页面解析
-
执行: 不是 并行的, 会打断 页面解析
- 加载和执行,★ 都会 打断页面解析
-
② 异步加载
async
的 加载和执行 过程- 加载:是并行的,不影响 页面解析
-
执行: 不是并行的, 会打断 页面解析
- 加载 不打断页面解析,★执行 会打断页面解析.
-
③ 延迟执行
defer
的 加载和执行 过程- 加载:是并行的,不影响 页面解析
- 执行: 不是并行的, 但不会打断 页面解析,因为是在 页面解析完成后 才执行的.
- 加载和执行,★都不会 打断页面解析.
-
⑵ 同时 指定
async,defer
属性: (行为回退,避免 阻塞行为)-
行为 回退: 即使指定了 异步加载
async
属性,也可以指定defer
属性-
回退到 延迟执行: 以使 仅支持 延迟执行
defer
(而不支持 异步加载async
)的遗留Web浏览器 退回到 延迟执行defer
行为,而不是 默认的阻塞行为。
-
回退到 延迟执行: 以使 仅支持 延迟执行
-
行为 回退: 即使指定了 异步加载
-
⑶ 多个脚本的 执行顺序:
- ① 同步加载
-
按顺序: 放置在
<head>
内- 会阻塞
<body>
的渲染, 会出现白屏,按照顺序 立即执行几个脚本文件
- 会阻塞
-
★ 按顺序: 放置在
<body>
底部- 等
<body>
中的内容 渲染完毕后, 再加载, 顺序执行js
-
<script>
写在<body>
底部,没有 兼容性问题,没有 白屏问题,没有 执行顺序问题.
-
- 是比较推荐的写法,很多网站都在使用,比如 淘宝网, MDN 网站等.
- 等
-
按顺序: 放置在
- ② 异步加载: 放置在
<head>
头部并使用async
- 先加载完毕的 先执行: 异步 并行加载资源,且加载完JS资源立即执行,并不会按 js 文件的顺序,谁快 谁先上
- ③ 延迟执行: 放置在
<head>
头部并使用defer
-
按 顺序执行: 异步 并行加载资源,在 DOM 渲染后,即,页面解析完毕后, 再按顺序执行
js
- HTML5 规范要求 脚本按照它们出现的 先后顺序执行,因此 第一个
defer
延迟脚本, 会先 于第二个延迟脚本执行,而这两个脚本 会先于DOMContentLoaded
事件执行。 - 在实际应用当中,延迟执行的脚本 并不一定会按照 顺序执行,也不一定会在
DOMContentLoad
时间触发前 执行,因此最好只包含一个延迟脚本。
-
按 顺序执行: 异步 并行加载资源,在 DOM 渲染后,即,页面解析完毕后, 再按顺序执行
- ④ ★ 异步加载 和延迟执行: 放置在
<head>
头部, 并同时使用async
和defer
- 表现和
async
一致 - 不支持
async
的,支持defer
的,会表现得和defer
一样- 异步加载 比延迟执行的 优先级高些.
- 这种方法,也是比较推荐的,同时兼容 支持
async
和defer
两个属性的 浏览器
- 表现和
- ① 同步加载
-
⑷ 仅限 外部脚本: 脚本
<script>
标签的 异步加载async
和 延迟执行defer
,都必须搭配src
属性,仅限 外部脚本 使用.
-
示例1: 在 MDN 网站中,也出现了 同时设置 异步加载和延迟执行的
<script>
标签.
结束语
- 参考文章
- 欢迎指正: 如果发现 有需要 更正的细节问题,欢迎指正,谢谢大家 ~╮( ̄▽ ̄)╭
-
转载 请注明出处 ,Thanks♪(・ω・)ノ
- 作者:Hey_Coder
- 来源:CSDN
- 原文:https://blog.csdn.net/VickyTsai/article/details/102841293
- 版权声明:本文为博主原创文章,转载请附上博文链接!