script 标签
<script> 标签是用于将 JavaScript 代码插入到 HTML 的主要方法。它具有内联和外部形式两种使用方式。
当 <script> 标签具有 src 属性的时候,标签内的代码会被忽略。
async 和 defer 是 <script> 标签的可选属性。对于非模块脚本,只对外部脚本文件有效;对于模块脚本,async/defer 属性也适用于内联脚本。获取 async/defer 脚本的网络请求是异步的,不会阻塞浏览器解析 HTML。
html 解析 与 async/defer 的执行的顺序:
async
async 脚本保证会在页面的 load 事件之前执行,但可能会在 DOMContentLoaded 之前或之后执行,取决于网络请求快慢。
- 网络请求完成之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先执行 JS 脚本代码,执行完毕后再解析 HTML。
- 如果在脚本请求完成之前,HTML 已经解析完毕了,那就会立即执行 JS 脚本代码。
如果存在多个 async 脚本的时候,它们之间的执行顺序不确定,完全取决于谁先下载完毕。
对于 XHTML 文档,指定 async 属性时应该写成 async="async"。
defer
defer 脚本在 DOMContentLoaded 事件之前执行。
- 网络请求完成之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析 HTML,而是等待 HTML 解析完毕再执行 JS 代码。
HTML5 规范要求脚本应该按照它们出现的顺序执行。
对于 XHTML 文档,指定 defer 属性时应该写成 defer="defer"。
在 esm 脚本中的表现形式
浏览器对于带有 type="module" 的模块脚本的加载规则与 <script defer> 的加载规则一致。当 HTML 解析到 <script type="module"> 标签后不会阻塞解析,并延迟到文档解析完成之后执行相应代码。
如果网页有多个 <script type="module">,它们会按照在页面出现的顺序依次执行。
如果在模块脚本里加上 async 属性,当所有依赖加载完成后立即停止 HTML 解析并运行相应代码。
<!-- 所有依赖都获取完成(analytics.js)然后脚本开始运行 -->
<script async type="module">
import {counter} from './analytics.js';
counter.count();
</script>