nomodule 只对不支持 ES 模块语法的浏览器(如 IE、Edge)生效,不是为旧版浏览器提供降级的万能开关。

nomodule 不是为“旧版浏览器”提供降级的万能开关,它只对**不支持 ES 模块语法的浏览器**生效——比如 IE、Edge nomodule 的 <script>;而老浏览器根本解析不了 type="module",所以才需要这个配对机制。
为什么不能只靠 nomodule 判断“是否老旧”
很多开发者误以为加了 nomodule 就等于“给 IE 加兼容脚本”,其实不是:
- IE 11 完全不识别
nomodule属性,但它会执行所有不带type="module"的<script>—— 所以你必须确保降级脚本本身是 ES5 语法、无 Promise/async/arrow function 等特性 - 某些安卓 WebView 或定制内核可能声称支持
type="module",但实际有 bug;它们可能错误地跳过nomodule脚本,导致降级失效 -
nomodule只控制脚本是否执行,不解决 polyfill 注入、CSS 兼容、API 可用性等其他问题
正确配对:type="module" 和 nomodule 必须成对出现
典型写法是两行 script 标签,顺序和属性都不能错:
<script type="module" src="app.mjs"></script> <script nomodule src="app.es5.js"></script>
注意:
立即学习“前端免费学习笔记(深入)”;
- 两个
<script>必须都放在同一位置(比如都放在<body>底部),否则加载顺序或执行时机可能出问题 - 不要给
nomodule脚本再加type="text/javascript"—— 老浏览器默认就是该类型,显式声明反而在某些旧 Android 上引发解析异常 - 确保
app.es5.js文件里没有import/export,且所有语法经 Babel 转译到 ES5,并已注入core-js或regenerator-runtime(如需 Promise、Array.from 等)
常见错误:polyfill 加载时机不对
如果降级脚本依赖 Polyfill.io 或手动引入 core-js,容易踩坑:
- 把
<script nomodule src="https://polyfill.io/v3/polyfill.min.js?features=Promise,URL,fetch"></script>单独放一行,但没控制加载顺序 —— 导致app.es5.js在 polyfill 就绪前就执行,报Promise is not defined - 用
defer或async在nomodule脚本上 —— 老浏览器不保证执行顺序,app.es5.js可能先于 polyfill 运行 - 正确做法:把 polyfill 和业务脚本合并打包(如用 Webpack +
@babel/preset-env+useBuiltIns: 'usage'),或用document.write动态注入(仅限nomodule环境下安全使用)
真正麻烦的从来不是 nomodule 属性本身,而是你默认它“兜底”了整个兼容链——从 JS 语法、全局 API、CSS 特性到第三方库行为,它只管其中一环。漏掉 polyfill、忘了 CSS @supports 回退、或者用了 Object.assign 却没补丁,都会让降级脚本在 IE 里当场崩溃。



















