script 标签的异步加载:async、defer、type="module" 详解

script 标签的异步加载:async、defer、type="module" 全面解析

本文主要是分享一下,关于加载script多种方式及区别

  • 1.同步加载:<script src="index.js"></script>
  • 2.defer 异步加载:<script defer src="index.js"></script>
  • 3.async 异步加载:<script async src="index.js"></script>
  • 4.module 异步加载:<script type="module" src="index.js"></script>
  • 5.module async 异步加载:<script type="module" src="index.js" async></script>

众农周知,浏览器解析html时,如果遇到使用 <script src="index.js"></script> 加载js时,html的解析会被阻塞,这个时候会先去下载index.js,等待下载完并执行完js之后,然后再继续解析html。这样的话,如果js下载时间过长,无疑会增加页面的渲染时间,那么,有没有啥办法可以让解析html与下载js并行执行呢?有的,并且提供了多种。

1.五种加载方式深度解析

1.1 同步加载 <script src="index.js"></script>

特点
  • 阻塞解析html
  • 须等加载完js并执行完之后才能继续解析html
  • 增加首屏渲染时间

不推荐使用

适用场景
  1. 传统的加载script方式,需要兼容低版本浏览器
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 同步加载且阻塞解析html -->
   <script src="index.js"></script>
  </head>
  <body>
  </body>
</html>

1.2 异步加载,使用 defer属性

特点
  • 并行下载js
  • 不阻塞解析html,解析html与下载并行
  • 在html解析完成DOMContentLoaded事件触发执行
  • 多个script时,异步加载,按顺序执行
适用场景
  1. 加载的js存在依赖关系,须按顺序执行
  2. 加载的js需要操作DOM
  3. 常见vue、react项目中,构建工具已自动处理,编译后自动添加defer属性
html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="icon" href="/test/favicon.ico" />
    <title>xxxxx管理系统</title>
    
    <!-- 异步加载,优先执行第三方依赖 -->
    <script defer="defer" src="/test/js/chunk-vendors.0821bbb1.js"></script>
    <!-- 异步加载,上面的js执行完再执行 -->
    <script defer="defer" src="/test/js/app.566a7ad5.js"></script>
    
    <link href="/test/css/chunk-vendors.ff7db5f3.css" rel="stylesheet" />
    <link href="/test/css/app.0d1bd94e.css" rel="stylesheet" />
  </head>

  <body>
    <noscript
      ><strong
        >System doesn't work properly without JavaScript enabled. Please enable
        it to continue.</strong
      ></noscript
    >
    <div id="app"></div>
  </body>
</html>

1.3 异步加载,使用 async属性

特点
  • 并行下载js
  • 下载阶段,不阻塞解析html,解析html与下载并行
  • 下载完成后马上执行
  • 多个script时,异步加载,谁先下载完谁就执行
  • 可能阻塞解析html,如果解析html完成前就已经下载完了,那就会执行js,阻塞解析html
  • 可能会阻塞 DOMContentLoaded 事件的触发时机 当js在 DOMContentLoaded 事件触发前完成下载并执行,就会延长 DOMContentLoaded的触发时间,js的执行会阻塞主线程,执行完才会触发 DOMContentLoaded事件
适用场景
  1. 加载的js没有赖关系,单独执行
  2. 加载的js无需操作DOM
  3. 常用于分析、广告、埋点脚本加载
html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="icon" href="/test/favicon.ico" />
    <title>xxxxxx官网</title>
    
    <!-- 异步加载,独立运行,无脚本依赖 -->
    <script async src="https://www.guanggao.com/gg/js?id=GG_1"></script>
    <script async src="https://www.guanggao.com/gg/js?id=GG_2"></script>
  </head>

  <body>
    <noscript
      ><strong
        >System doesn't work properly without JavaScript enabled. Please enable
        it to continue.</strong
      ></noscript
    >
    <div id="app"></div>
  </body>
</html>

1.4 异步加载,使用 type="module"属性

特点与defer差不多
  • 不阻塞解析html,解析html与下载并行
  • 在html解析完成DOMContentLoaded事件触发执行
  • 多个script时,异步加载,按顺序执行
  • defer主要差异是,使用esm模块化,且默认开启严格模式
适用场景 与defer差不多
  1. 加载的js存在依赖关系,须按顺序执行
  2. 加载的js需要操作DOM
  3. 使用esm开发的脚本
  4. 常见使用vite构建的vue、react项目中,编译后自动添加type="module"属性
html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="icon" href="/test/favicon.ico" />
    <title>xxxxxx系统</title>
    
    <!-- 异步加载,使用esm开发 -->
    <script type="module" crossorigin src="/assets/index-BmxZ5FsG.js"></script>
  </head>

  <body>
    <noscript
      ><strong
        >System doesn't work properly without JavaScript enabled. Please enable
        it to continue.</strong
      ></noscript
    >
    <div id="app"></div>
  </body>
</html>

1.5 异步加载,使用 type="module" async属性

特点与async差不多
  • 并行下载js
  • 下载阶段,不阻塞解析html,解析html与下载并行
  • 下载完成后马上执行
  • 多个script时,异步加载,谁先下载完谁就执行
  • 可能阻塞解析html,如果解析html完成前就已经下载完了,那就会执行js,阻塞解析html
  • 可能会阻塞 DOMContentLoaded 事件的触发时机 当js在 DOMContentLoaded 事件触发前完成下载并执行,就会延长 DOMContentLoaded的触发时间,js的执行会阻塞主线程,执行完才会触发 DOMContentLoaded事件
  • async主要差异是,使用esm模块化,且默认开启严格模式``
适用场景 与async差不多
  1. 加载的js没有赖关系,单独执行
  2. 加载的js无需操作DOM
  3. 常用于分析、广告、埋点脚本加载
html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="icon" href="/test/favicon.ico" />
    <title>xxxxxx官网</title>
    
    <!-- 异步加载,使用esm开发,独立运行,无脚本依赖 -->
    <script type="module" async src="https://www.guanggao.com/gg/js?id=GG_1"></script>
  </head>

  <body>
    <noscript
      ><strong
        >System doesn't work properly without JavaScript enabled. Please enable
        it to continue.</strong
      ></noscript
    >
    <div id="app"></div>
  </body>
</html>

2.总结

2.1.加载script的5种方式:

  • 同步-阻塞解析html: <script>
  • 异步-不阻塞解析html-按顺序执行: <script defer>
  • 异步-不阻塞解析html-无序下载完立即执行: <script async>
  • 异步-esm-不阻塞解析html-按顺序执行: <script type="module">
  • 异步-esm-不阻塞解析html-无序下载完立即执行: <script type="module" async>

2.2.各方式差异:

加载方式 执行时机 是否阻塞HTML解析 执行顺序 适用场景
<script> 立即执行 文档顺序 传统脚本
<script defer> DOM解析后 文档顺序 依赖DOM的脚本
<script async> 下载完成后 下载不阻塞,执行可能阻塞 无序 独立第三方脚本
<script type="module"> DOM解析后 文档顺序 ES模块化代码
<script type="module" async> 下载完成后 下载不阻塞,执行可能阻塞 无序 独立ES模块

2.3.加载流程图:

2.4.使用建议:

  • js使用非esm,优先考虑使用 defer
  • js使用esm,优先考虑使用 type="module"
相关推荐
乐吾乐科技11 小时前
乐吾乐3D可视化2025重大更新与2026升级计划
前端·3d·信息可视化·编辑器·数据可视化
C_心欲无痕11 小时前
html - 使用视频做天气卡片背景
前端·html·音视频
毕设十刻11 小时前
基于Vue的养老服务平台85123(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
青衫折扇11 小时前
执行 npm 安装命令时,包被装到了 C 盘用户目录下,而非项目根目录
前端·npm·node.js
XiaoYu200211 小时前
第2章 Nest.js入门
前端·ai编程·nestjs
2501_9462447812 小时前
Flutter & OpenHarmony OA系统下拉刷新组件开发指南
开发语言·javascript·flutter
没事多睡觉66612 小时前
零基础React + TypeScript 教程
前端·react.js·typescript
liliangcsdn12 小时前
MySQL存储字节类数据的方案示例
java·前端·数据库
_Kayo_12 小时前
React useState setState之后获取到的数据一直是初始值
前端·javascript·react.js
谷哥的小弟12 小时前
HTML5新手练习项目—生命体征监测(附源码)
前端·源码·html5·项目