script type="module"通过静态分析import构建依赖图并按拓扑序执行,确保依赖模块先于引用者加载;重复导入同一模块仅执行一次,实现单例与作用域隔离,但必须配合export/import语法才生效。

script type="module 是浏览器原生支持的依赖管理机制,不是“能用”,而是“必须用”——只要项目里有多个 JS 文件且存在调用关系,手动维护 <script> 顺序就大概率出错。
为什么 script type="module" 能自动解决依赖顺序问题
传统 <script src="a.js"></script> 和 <script src="b.js"></script> 没有语义化依赖声明,浏览器只按 HTML 中出现顺序加载执行;而模块脚本会静态分析 import 语句,构建依赖图,再按拓扑序加载:
- 即使
b.js在 HTML 中写在a.js前面,只要b.js里写了import { foo } from './a.js',浏览器就一定先加载并执行a.js - 重复 import 同一个模块(比如
c.js和d.js都 import'./utils.js'),浏览器只执行一次utils.js,后续返回缓存的模块实例 - 模块内顶层代码只执行一次,函数/类不会被重复定义,彻底避免全局变量冲突
type="module" 必须配合 export/import 才生效
光加 type="module" 不够,模块脚本内部若没用 export 导出、也没用 import 引入,它只是个“带作用域隔离的普通脚本”,起不到依赖管理作用:
- 错误写法:
<script type="module" src="main.js"></script>,但main.js里全是var x = 1; console.log(x);—— 没有import或export,依赖图为空,等同于带defer的普通脚本 - 正确写法:入口模块(如
main.js)至少有一条import,被导入的模块(如api.js)至少有一条export - 注意:
export default和具名export不能混用在同一个文件里直接解构,否则报SyntaxError: Unexpected token 'export'
不支持 type="module" 的老浏览器怎么 fallback
IE 和低于 Chrome 61 / Firefox 60 的版本不识别 type="module",会忽略该标签;这时要用 nomodule 提供降级方案:
立即学习“前端免费学习笔记(深入)”;
- 必须成对使用:
<script type="module" src="app.mjs"></script>+<script nomodule src="app.fallback.js"></script> -
nomodule脚本仅在不支持模块的浏览器中执行,现代浏览器会跳过它 - 不要把
nomodule脚本放在type="module"之前——某些旧版 Safari 会同时加载两个,造成重复执行 - fallback 脚本不能假设模块环境(比如不能用
import),得是纯 CommonJS 或 IIFE 形式
动态 import() 是模块依赖管理的延伸能力
静态 import 在解析时就确定全部依赖,适合初始化逻辑;而 import() 是运行时按需加载,用于真正“拆分”的场景:
- 路由切换时加载对应页面模块:
const module = await import(`./pages/${route}.js`) - 用户点击按钮后才加载图表库:
button.addEventListener('click', () => import('chart.js').then(...)) - 注意:
import()返回 Promise,不能在模块顶层 await;必须包在函数或事件回调里 - 打包工具(如 Webpack/Vite)能识别
import()并自动代码分割,但原生浏览器不提供 chunk 预加载提示,需手动加<link rel="modulepreload">
模块依赖管理真正的难点不在写法,而在路径和导出一致性:相对路径拼错、大小写不匹配、默认导出和具名导出混用,都会导致 import 失败且错误信息模糊。建议所有模块路径用 .js 后缀显式声明,导出统一用具名形式,避免 export default 和命名冲突。



















