点击关注公众号,“技术干货” 及时达!
前言
回想起当年面试时面试官问我:你简历上说熟练HTML
,那么说说script
的async、defer
的区别吧。当时一下就蒙了,心想工作中好像从来没有用到过啊,那次之后我细细查阅资料才彻底理解它们的作用和区别。
这算是很经典面试题了,这面试题目没有难度,但却可以体现一个前端的基本功水平,因此下面除了文字讲解外还会配合官方的图片和博主自定义的示例,便于更深刻理解。
❝先汇总一下在
HTML
中的script
会有这三种用法,然后下面一一说明。❞
默认按顺序执行用法: <script src='https://...'></script>
脚本下载完马上执行用法: <script src='https://...' async></script>
脚本下载完最后执行用法: <script src='https://...' defer></script>
script
「解释:」 当浏览器正在执行解析 HTML
代码块时,如果遇到默认的 script
标签,就会阻塞从而暂停解析 HTML
的剩余代码,去加载该 javascript
脚本,脚本加载完成之后会立即执行脚本,最后继续解析 HTML
代码。
配合下图理解更直观。
再看看如下示例代码解释
<html lang="zh">
<head>
<script>
console.log("Hello log~");
</script>
<script lay-src="https://.../Chart.min.js"></script>
<script lay-src="https://.../moment.min.js"></script>
<script lay-src="https://.../vue.min.js"></script>
</head>
<body>
您好,我是天天鸭的示例1
</body>
</html>
❝执行顺序会如下:
❞
执行打印: console.log("Hello log~")
;下载并执行 Chart
外部脚本:Chart.min.js
下载并执行 moment
外部脚本:moment.min.js
下载并执行 vue
外部脚本:vue.min.js
显示文本内容: 您好,我是天天鸭的示例1
「注意:」 默认script
会按顺序执行脚本,如果中间有一个脚本执行时间长, 后续的脚本就会排队,HTML
也停止解析等待执行完成,这时会有白屏情况出现。
script async
「解释:」 当浏览器正在执行解析 HTML
代码块时,如果遇到 script
标签并且标识是async
,不会阻塞解析 HTML
的剩余代码, 而是异步请求去加载该 javascript
脚本,但脚本加载完成之后会立即执行脚本并且此时会阻塞停止解析HTML
,最后执行完脚本继续解析 HTML
代码。
配合下图理解更直观。
再看看如下示例代码解释
<html lang="zh">
<head>
<script>
console.log("Hello log~");
</script>
<script async lay-src="https://.../Chart.min.js" ></script>
<script async lay-src="https://.../moment.min.js"></script>
<script async lay-src="https://.../vue.min.js"></script>
</head>
<body>
您好,我是天天鸭的示例2
</body>
</html>
❝执行顺序会如下:
❞
执行打印: console.log("Hello log~")
;异步下载 Chart.min.js、moment.min.js
和vue.min.js
脚本,此时HTML
正常解析中下载完开始脚本,此时阻塞HTML解析 最后接着解析剩下 HTML
「注意:」 带async
标识的script
会在执行脚本时阻塞HTML
解析,但下载脚本时不会,所以如果页面HTML
结构比较简单那么会在脚本下载过程中就已经执行完成,因此上述例子是屏幕先显示您好,我是天天鸭的示例2
,再执行脚本。
如果HTML
结构复杂且量大解析时间较长,会在脚本执行时中断,执行完成接着解析。
script defer
「解释:」 当浏览器正在执行解析 HTML
代码块时,如果遇到有 script
标签并且标识是defer
,不会阻塞解析 HTML
的剩余代码, 而是异步请求去加载该 javascript
脚本,但脚本加载完成之后并不会立即执行脚本,直到最后解析完 HTML
代码,最后再去执行脚本。
配合下图理解更直观。
再看看如下示例代码解释
<html lang="zh">
<head>
<script>
console.log("Hello log~");
</script>
<script defer lay-src="https://.../Chart.min.js" ></script>
<script defer lay-src="https://.../moment.min.js"></script>
<script defer lay-src="https://.../vue.min.js"></script>
</head>
<body>
您好,我是天天鸭的示例3
</body>
</html>
❝执行顺序会如下:
❞
执行打印: console.log("Hello log~")
;异步下载 Chart.min.js、moment.min.js
和vue.min.js
脚本,此时HTML
正常解析中下载完脚本后不会立即执行,等待 HTML
全部解析完显示文本内容:您好,我是天天鸭的示例3最后执行脚本
「注意:」 如果多个defer
脚本,浏览器会并行下载,无论下载速度的快慢都不影响执行的顺序,会按HTML
中出现的顺序依次执行。
混合使用
真实场景可以会几种方法混用,示例如下。
<html lang="zh">
<head>
<script>
console.log("Hello log~");
</script>
<script defer lay-src="https://.../Chart.min.js" ></script>
<script async lay-src="https://.../moment.min.js"></script>
<script defer lay-src="https://.../vue.min.js"></script>
</head>
<body>
您好,我是天天鸭的示例4
</body>
</html>
❝执行顺序会如下:
❞
执行打印: console.log("Hello log~")
;异步下载 Chart.min.js、moment.min.js
和vue.min.js
脚本因为HTML比较简单,脚本下载过程中就执行完成了,显示文本内容:您好,我是天天鸭的示例4。 moment.min.js
脚本下载完成后马上执行Chart.min.js
和vue.min.js
会在HTML解析完成后执行
小结
原生 script
阻塞HTML
的解析,按顺序执行defer
不阻塞HTML
的解析,并且脚本按HTML
的顺序执行,HTML
解析完才执行脚本async
有可能阻塞HTML
的解析,脚本按网络请求顺序执行,谁先下载完就先执行谁,不可控
因此,理论上如果脚本独立的与其它资源加载没有关联可以选择async
(可能会阻塞), 但脚本之间存在依赖关系需要选择defer
「注意:」 如果同时存在async,defer
属性,async
优级更高
好啦先写到这时,如果那里写的不好或者不对,欢迎指出。
点击关注公众号,“技术干货” 及时达!