单独使用 type="module" 会导致 ie11 白屏,因其不识别该属性而静默丢弃 script 标签,既不加载也不报错;必须配 nomodule 脚本提供降级路径,否则旧浏览器无 js 可执行。

type="module" 本身不降级,它只是个“开关”——支持的浏览器执行它、跳过 nomodule;不支持的浏览器直接忽略它、转而执行 nomodule 脚本。只写 type="module" 而不配 nomodule,旧浏览器就彻底没 JS 可执行。
为什么单独用 type="module" 会导致 IE11 白屏
IE11、Edge 12–18、Safari 10.1 之前等浏览器不认识 type="module" 这个值,解析时静默丢弃整个 <script></script> 标签:不下载、不解析、不报错、也不 fallback 到 text/javascript。如果你只写了:
<script type="module" src="app.mjs"></script>
那这些浏览器里,app.mjs 就根本不会加载,页面 JS 功能全失效。
- 常见错误现象:开发者工具 Network 面板里看不到脚本请求,Console 也无报错,但交互完全失灵
- 别信“现代浏览器自动兼容”这种说法——它不自动做任何事,必须你显式声明降级路径
- Webpack/Vite 默认生成的 HTML 已含双标签结构,但手动维护或 SSR 场景下极易漏掉
nomodule
nomodule 不是可选属性,是降级逻辑的核心开关
nomodule 是个布尔属性,**只被支持 type="module" 的浏览器识别并主动跳过**。它的作用不是“告诉旧浏览器该执行什么”,而是“告诉现代浏览器:这个脚本你别碰”。所以它必须和 type="module" 成对出现才有意义。
典型写法:
<script type="module" src="modern.mjs"></script><br><script nomodule src="legacy.js"></script>
-
modern.mjs应含import/export,且不能有document.write(模块默认defer) -
legacy.js必须是 IIFE 或 UMD 格式,无 ES 模块语法,可直接执行(通常由 Babel + core-js 构建) - 两个
<script></script>标签顺序无关,但建议模块在前——避免旧浏览器先加载大体积legacy.js后又因缓存/网络问题卡住 - Safari 10.1 是分水岭:它会执行
legacy.js;Safari 11+ 才真正支持模块并跳过nomodule
构建和部署时容易被忽略的三个坑
双脚本方案看似简单,但实际落地常因构建配置或运行时细节翻车:
- 输出的
legacy.js必须包含所有 polyfill(如Promise、Array.from),且不能依赖import—— 否则在 IE11 里会 SyntaxError - 如果用动态
import(),旧浏览器无法识别,必须在legacy.js中用require.ensure或代码分割兜底(Webpack 4+ 需配target: ["web", "es5"]) -
nomodule脚本若带async或defer,在旧浏览器中仍按传统脚本同步执行,可能阻塞渲染;建议只对type="module"脚本用async,nomodule保持无修饰
最麻烦的不是写对双标签,而是确保两套产物功能完全对等:modern.mjs 里用的 fetch,legacy.js 里得有 whatwg-fetch;modern.mjs 里解构赋值,legacy.js 里得能跑通 Babel 转译后的等效代码。一旦漏掉某个 API 或行为差异,降级就变成“功能阉割”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











