惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

胡方运的博客

macOS App 开发 iCloud 不同步问题解决 机票还能这么买!随心飞低价旅行攻略 提前避坑:父母患病和医保问题 我和 Apple 开发的缘分 我的手机流量和宽带套餐性价比方案 iMessage 在国内好用吗? 为什么要写博客 macOS 升级 beta 系统后,如何打开原来版本的 XCode Xcode 安装依赖超时问题解决办法 短信转发-多设备多平台 Follow RSS 阅读 App 记录一次服务器重启应用恢复过程 macOS 重装踩坑记 我的 2021 我的 2020 年终总结 如何给网站添加免费的 https 证书 如何管理多个 git 账号 写给自己的JavaScript系列之一步步写个 JavaScript 解释器 如何修改 git 已提交的用户名和邮箱
JavaScript 文件的异步加载 async 和 defer
感谢您的阅读。 🙏 关于转载请看这里 · 2018-11-05 · via 胡方运的博客

背景

在 webpack 等工具的照料下,我们忘记了很多的东西,比如说 JavaScript 文件的异步加载,异步执行等等内容。唯一能记起是关于 script 的优化可能是把 script 标签放到页面底部。

某次你看到 ‘‘ 这样的引入语句,请问这 async 到底是什么意义呢?

任务

1、学习 async 的作用,使用限制等。

2、有没有其他类似的属性呢?

技能

性能优化

开始

聊 JavaScript 文件的异步加载之前,我们先来看一段 HTML 文档的加载流程。

<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://a.com/a.js">
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
这里有很多的 HTML 标签和内容
</body>
</html>

对于上面的 HTML 文档,浏览器的解析过程如下:

1、浏览器一边下载 HTML 网页,一边开始解析(并不是下载完之后,才开始开始解析)。

2、解析过程中,浏览器发现 <script> 标签就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎。

3、如果 <script> 标签引用了外部脚本,就下载该脚本再执行,否则就直接执行代码。

4、JavaScript 引擎执行完毕,控制权交还渲染引擎,继续解析 HTML 网页。

加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后再继续渲染。

这就是为什么我们一般建议把 <script> 标签放在 </body> 之前,而不是放在 <head></head> 中,因为加载 <script> 中的脚本并执行会阻塞 HTML 页面的渲染,当 JavaScript 文件很大的时候,甚至会长时间出现白屏。

有没有什么办法可以优化 JavaScript 文件的加载执行,提高页面加载的效率?

解读 async 和 defer 属性

翻阅 HTML5 的文档 就会发现,<script> 还有 asyncdefer 两个和脚本加载执行有关的属性。

async - Execute script in parallel //并行执行脚本

defer - Defer script execution // 延迟执行脚本

HTML4.1是这么解释 defer 的:

When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no “document.write” in javascript) and thus, the user agent can continue parsing and rendering.

就是说浏览器在遇到设置了 defer 的 <script> 时,加载了 JavaScript 文件后,不会立即执行,不会阻塞浏览器解析 HTML,而且如果设置了 defer 不要在 JavaScript 脚本里写 document.write

HTML5.1这么解释 defer 和 async 的:

此处省略将近 1500 字的英文引用,感兴趣点击的 HTML5.1 文档。

对省略的原文总结如下:

1、defer 和 async 只对外部加载的脚本有效果,<script> 包含的 JavaScript 代码块无效。

2、必须设置了 src 属性,不然 defer 和 async 也无效。

3、defer 和 async 这两个值是布尔类型的。

4、如果设置了 async 属性,会并行加载脚本文件并执行,下载时不会阻塞 HTML 的解析,但是脚本执行的时候会阻塞 HTML 的解析。如果没有设置 async 属性,但是设置了 defer 属性,也会并行加载脚本文件,但是会等到页面完成解析再去执行。如果这两个属性都没有设置,会阻塞页面解析,加载并执行脚本文件。三种方式的对比图如下:

图片来源:www.w3c.org图片来源:www.w3c.org

HTML5.3的文档把第四条补充了一下。。。

1、defer 对 module 脚本是无效的,但是 async 是有效的

2、如果同时设置了 defer 和 async 为 true, 以 defer 为准

小总结

通过给 <script> 标签设置 defer 属性,将脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 <script> 标签时,会再开启一个线程去下载 JavaScript 文件,同时继续解析 HTML 文档,等 HTML 全部解析完毕 DOM 加载完成之后(也就是DOMContentLoaded 事件之后 onload 事件之前),再去执行加载好的 JavaScript 文件。多个js文件的执行顺序就是它们在页面中出现的顺序。

async 属性和 defer 属性类似,也是会开启一个线程去下载 JavaScript 文件,但和defer 不同的是,它会在下载完成后立刻执行,而不是会等到 DOM 加载完成之后再执行,所以还是有可能会造成阻塞。对多个带有 async 的 JavaScript 文件,它不能像defer 那样保证按顺序执行,它是哪个 JavaScript 文件先下载完就先执行哪个。

使用

什么时候用 defer,什么时候用 async 呢?

一般来说,两者之间的选择则是看脚本之间是否有依赖关系,有依赖的话应当要保证执行顺序,应当使用 defer 没有依赖的话使用 async。要注意的是两者都不应该使用 document.write,这个导致整个页面被清除。

模块化的代码、测试代码或者监听代码使用 async。

这两个值是布尔类型的,在 HTML 里面只写属性名即可。

<script src="https://a.com/a.js" async>

如果不考虑兼容 IE 浏览器,完全可以将 <script> 放在 <head> 里,同时守设置 defer 属性,这样 HTML 解析的时候,可以并行下载 JavaScript 脚本,等 HTML 页面解析完,JavaScript 脚本在开始执行,充分利用资源。比如查看 gitlab 页面源码。

兼容性

defer 是 HTML4 就有的属性,而 async 是 HTML5 新加入的属性。defer 出现的早,IE6 及以上的浏览器基本都支持,可以放心用。 async IE10 就开始支持了,移动端的支持比较好。不考虑 IE 的话,两个属性都可以放心使用。

兼容性可以点击这里查看caniuse

测试

如果同时设置了 defer 和 async 为 true,浏览器会怎么执行?

我们做个小实验。

新建一个文件夹,文件结构如下:

.
├── 1.js
├── 2.js
└── index.html

1.js 如下:

alert(1);

2.js 如下

alert(2);

index.html 的内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script defer async src="./1.js"></script>
<script async defer src="./2.js"></script>
<title>测试 async 和 defer </title>
</head>
<body>

<script>
alert(3);
</script>
</body>
</html>

设置两个 js 是为了排除 async 和 defer 先后顺序的影响。

FireFox、Chrome、Safari、三个浏览器的执行结构都是 3、1、2,说明同时设置了 defer 和 async 为 true, 以 defer 为准。

扩展阅读

async vs defer attributes

参考

网页性能优化之异步加载js文件 - 掘金

HTML4.1

HTML5.1

HTML5.3