前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库script nomodule 降级方案注意点
JavaScript模块与语言机制

用 type="module" 加 nomodule 给老浏览器降级,要注意哪些坑?

写两个标签:<script type="module" src="modern.js"> 给新浏览器,<script nomodule src="legacy.js"> 给老浏览器。支持模块的浏览器会跳过带 nomodule 的脚本,老浏览器不认识 type="module" 所以跳过它,只跑 legacy。要注意执行时机不同、少数浏览器会重复下载甚至重复执行。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#模块与语言机制#构建工具
先看核心答案读代码示例
理解线索

两个标签各自被谁执行

  1. type="module"支持 ESM 的浏览器执行,老浏览器当成未知类型忽略
  2. nomodule支持 ESM 的浏览器不执行,老浏览器不认识这个属性照常执行
  3. 现代包和兼容包同一份源码构建两次,目标环境和 polyfill 不同

这个方案靠的是「支持 ESM」这一个信号去猜浏览器的整体能力。支持 type="module" 的浏览器里,也有一些不支持动态 import() 或 import.meta,现代包里用了这些特性,就需要额外检测后再决定是否降级。

核心回答

先记住这个答案

降级方案是在页面上同时放两个标签:type="module" 加载现代代码,nomodule 加载转译过、带 polyfill 的兼容代码。支持 ESM 的浏览器看到 nomodule 会跳过它,只执行模块脚本;不支持 ESM 的老浏览器不认识 type="module",把它当成未知类型忽略,又不认识 nomodule 属性,于是正常执行兼容脚本。要注意三点:两类脚本执行时机不同;早期个别浏览器会两份都执行或都下载;支持模块不代表支持所有新语法。

  • 新浏览器执行模块并跳过 nomodule
  • 模块脚本默认延后执行
  • 早期个别浏览器两份都执行,或者两份都下载
  • 支持模块不等于支持所有新语法,现代包也要设好目标

标准写法和它为什么能生效

先看页面上的标签怎么写,再看新老浏览器各自会怎么处理。

现代包和兼容包配对HTML
<!-- 支持 ESM 的浏览器:执行这个,默认 defer -->
<script type="module" src="/assets/app.modern.js"></script>

<!-- 不支持 ESM 的浏览器:执行这个,加 defer 让时机和上面一致 -->
<script nomodule defer src="/assets/app.legacy.js"></script>

这套写法利用了两个「不认识就忽略」的规则。老浏览器遇到不认识的 type 值,会认为这不是 JavaScript,整个标签不处理;遇到不认识的属性,直接忽略属性,标签照常执行。新浏览器则明确知道 nomodule 的意思是「我支持模块,这个不用管」。 app.legacy.js 那一行加了 defer。模块脚本天生就是延迟执行的,等 HTML 解析完再按顺序运行;普通脚本不加 defer 会阻塞解析、下载完立即执行。两边时机不一致,代码里如果依赖 DOM 已经解析完,老浏览器上就可能出问题。

需要额外处理的几个坑

这套方案原理简单,但在真实浏览器里有几个已知的问题。

重复执行、重复下载和能力判断
早期有个别 Safari 版本支持了模块,却还没支持 nomodule,结果两份代码都执行,页面初始化两次。当时的通行做法是在页面里插一段小的检测脚本专门修正这个问题。 还有一些老版本的 IE 和 Edge,虽然只执行其中一份,却会把两份都下载下来,白白多一份流量。 第三个问题更隐蔽:type="module" 只说明浏览器支持 ESM,不说明它支持你现代包里用到的所有东西。比如较早支持模块的浏览器里,有一部分还不支持动态 import()。Vite 的 legacy 插件就会在页面里额外放一段检测,发现不支持这些特性时,也切换到兼容包。
回答前,多想一步

容易答错的地方

以为 nomodule 是给不支持 JS 的环境用的
它和 <noscript> 没关系。nomodule 只针对「能跑 JavaScript 但不支持 ES 模块」的浏览器,目的是给它们一份普通脚本。
以为现代包可以随便用最新语法
现代包的构建目标应该是「支持 ESM 的浏览器」这一批里最老的那些,而不是最新版 Chrome。用 browserslist 设定目标,让构建工具按目标转译,不要默认不转译。
两份包的全局副作用不一致
兼容包里带了 polyfill,会修改 Array.prototype 等全局对象;现代包没有。某段代码在老浏览器上能跑,是因为 polyfill 恰好补上了,换成现代包反而报错。测试时要两份都验。
试着用自己的话回答

面试官还会怎么问?

现在还有必要做这种降级吗?

看项目的用户群。面向大众的 C 端产品先看访问统计里老浏览器的占比,占比很低时很多团队已经直接不再构建兼容包,省下一半构建时间。政企、内网系统可能还有老版本 IE 内核,就得保留。

能不能只用 nomodule 做 polyfill 加载?

可以。只把 polyfill 放进 nomodule 脚本,业务代码用普通脚本并转译到老语法,这样新浏览器不用下载 polyfill。不过业务代码就得一直按老语法转译,享受不到现代包体积更小的好处。

服务端根据 UA 返回不同的包不是更准吗?

UA 判断能避免重复下载,但要维护 UA 规则库,还会和 CDN 缓存冲突,同一个 URL 要按 UA 缓存不同内容。module 加 nomodule 的好处是纯前端、无需服务端参与,缓存也简单。

从一道题,走向一组知识

把知识连起来

模块与语言机制

export ... from 再导出有哪几种写法?默认导出能被 export * 带出去吗?

现代包保留了 export ... from 这类模块语法,兼容包则要把它们全部转成普通脚本。

模块与语言机制

<script type="module"> 和普通 script 标签有哪些区别?

降级方案的执行时机问题,根源就在模块脚本默认延迟执行、普通脚本默认阻塞这个差异上。

参考资料

  • JavaScript:模块与语言机制参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 标准写法和它为什么能生效
  3. 需要额外处理的几个坑
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑