nomodule不是给旧浏览器开后门,而是让现代浏览器跳过它;旧浏览器因不识别该属性而执行它,但若无配套type="module"脚本则全白屏。

nomodule 不是给旧浏览器“开后门”的属性,而是让现代浏览器主动跳过它——旧浏览器反而会执行它,只因根本不认识这个属性。
为什么只写 nomodule 脚本会导致白屏
单独一行 <script nomodule src="fallback.js"></script> 在所有环境下都不可靠:
- 现代浏览器(Chrome 61+、Firefox 60+、Safari 11+)识别
nomodule,直接跳过,不发请求、不解析、不执行 - 旧浏览器(IE11、Android 4.4 WebView)不认识
nomodule,但也不支持type="module",所以如果没配对的type="module"脚本,它们连第一行都不会加载——结果新旧用户全白屏 - IE11 甚至会静默忽略整个
<script nomodule>标签,既不报错也不执行,你以为它在跑,其实什么都没干
type="module" 和 nomodule 必须成对出现
真正起作用的是组合逻辑,不是单个属性:
- 现代浏览器:看到
type="module"就加载执行app.mjs;同时看到nomodule就跳过第二行 - 旧浏览器:不认识
type="module",第一行被丢弃;不认识nomodule,第二行当作普通<script src>同步加载执行 - 正确写法必须是两行并存,顺序建议模块在前:
<script type="module" src="app.mjs"></script><br><script nomodule src="legacy-bundle.js"></script>
- 不要把
nomodule塞进 module 脚本里:<script type="module" nomodule src="app.js"></script>是无效写法——现代浏览器会跳过,旧浏览器又不认type="module",两边都落空
旧浏览器执行时的真实限制
nomodule 只管“要不要加载”,不管“能不能跑”。哪怕脚本成功进了 IE11,以下问题仍会立刻崩溃:
立即学习“前端免费学习笔记(深入)”;
- 语法层面:含
const、let、箭头函数=>、模板字符串`${x}`→ 直接SyntaxError - API 层面:用了
fetch()、Promise、Array.from、Object.assign→ReferenceError或静默失败 - DOM 层面:调用
element.classList.add()(IE9–)、element.querySelector()(IE7–)、addEventListener()(IE8–)→ 报错或无响应 - 构建配置必须显式设为
targets: { ie: "11" },不能依赖 Babel 默认 preset;core-js/stable需手动引入,且 polyfill 要在所有业务代码之前执行
Safari 10.1 的坑和绕过方案
Safari 10.1(macOS 10.12.4 / iOS 10.3)是个特例:它识别 type="module",却错误地也执行 nomodule 脚本,导致两套逻辑同时触发,UI 初始化两次、事件绑定重复、数据请求翻倍。
缓解方式是加一层运行时检测,只在“半吊子”浏览器中注入 fallback:
<script nomodule><br>if (!('noModule' in document.createElement('script'))) {<br> document.write('<script src="legacy-bundle.js"></script>');<br>}</script>
原理:'noModule' in HTMLScriptElement.prototype 在 Safari 10.1 返回 false(它实现了 type="module" 但没实现 nomodule 语义),于是进入 document.write 分支。其他现代浏览器返回 true,跳过;旧浏览器压根不执行这段 JS。
最常被忽略的一点:旧浏览器加载 nomodule 脚本是同步阻塞的,没有 defer 或 async 效果。哪怕你写了 <script nomodule defer src="...">,IE11 也会当它不存在——所以压缩体积、内联关键初始化、避免网络请求延迟,比纠结属性本身更重要。



















