Now vibe coding, so learning hammer FE ?
《你的模块在骗你:ESM 与 CommonJS 的本质差异与陷阱全解析》
标签:
#JavaScript #NodeJS #ESM #CommonJS #模块系统 #前端工程化
总结:
本文深入剖析了 ESM 和 CommonJS 两种模块系统在底层机制上的根本差异——ESM 通过"活绑定"(live bindings)在模块间共享变量引用,而 CommonJS 通过
文章要点:
1. 活绑定 vs 值复制:最经典的陷阱 — ESM 的
2. ESM 的导入会被提升,CommonJS 按执行顺序 — ESM 静态导入在模块体执行前就已经解析、链接并评估依赖,所以
3. 两种系统有独立的缓存 — ESM 和 CommonJS 各自维护自己的模块缓存,URL 查询参数(如
4. 循环依赖的处理方式天差地别 — CommonJS 允许在模块未完全初始化时就返回
5. 跨系统互操作的隐藏复杂性 — ESM 导入 CJS 时,
6. 双包实例风险(Dual-Package Hazard) — 当
7. 发布库的最佳实践 — 用
8. 调试模块问题的检查清单 — 从导入文件的格式、解析到的入口点、返回值形状、本地变量持有的是绑定还是副本、评估是否完成、是否存在循环依赖、模块身份是否一致、是否经过构建工具转换、是否通过打包后的 tarball 测试等 9 个维度系统排查。
9. 九条实践规则 — 新项目优先用 ESM;库优先用命名导出;可变导出视为共享进程状态;CJS 属性会变时保留对象而非解构;移除循环依赖而非绕开;条件/延迟加载用动态
URL:
https://blog.gaborkoos.com/posts/2026-08-14-Your-Modules-Are-Lying-to-You/
标签:
#JavaScript #NodeJS #ESM #CommonJS #模块系统 #前端工程化
总结:
本文深入剖析了 ESM 和 CommonJS 两种模块系统在底层机制上的根本差异——ESM 通过"活绑定"(live bindings)在模块间共享变量引用,而 CommonJS 通过
module.exports 返回一个普通值(通常是对象)。这一差异导致了同名导入在两种系统下行为截然不同:ESM 的命名导入能观察到导出模块的变量重新赋值,CommonJS 的解构赋值则只复制了属性的当前值。文章进一步系统讲解了评估顺序、缓存机制、循环依赖处理、跨系统互操作、双包实例风险等关键话题,并提供了 9 条实践规则和调试清单。文章要点:
1. 活绑定 vs 值复制:最经典的陷阱 — ESM 的
import { status } 是活绑定,导出模块重新赋值后导入方自动看到新值;CommonJS 的 const { status } = require() 是解构赋值,只复制了对象属性的当前值,后续变化不可见。只有保留整个对象 const state = require() 才能观察到属性变更。2. ESM 的导入会被提升,CommonJS 按执行顺序 — ESM 静态导入在模块体执行前就已经解析、链接并评估依赖,所以
import 写在代码中间也不影响执行顺序;CommonJS 的 require() 是普通函数调用,走到哪行才执行哪行。3. 两种系统有独立的缓存 — ESM 和 CommonJS 各自维护自己的模块缓存,URL 查询参数(如
?mode=one)会让同一文件被当作不同模块实例加载两次。条件导出("import" / "require")指向不同文件时,也会产生两个独立的模块实例。4. 循环依赖的处理方式天差地别 — CommonJS 允许在模块未完全初始化时就返回
exports 对象,因此循环依赖中的模块能看到对方"半成品"状态;ESM 则先创建绑定再执行模块体,如果在初始化前读取对方绑定会抛出 ReferenceError: Cannot access before initialization。5. 跨系统互操作的隐藏复杂性 — ESM 导入 CJS 时,
module.exports 作为 default 导出最可靠,命名导出是 Node 静态分析的"快照",不会跟踪后续属性变更;CJS 通过 require() 加载 ESM 时返回的是命名空间对象,需通过 .default 访问默认导出。__esModule 只是工具链约定,不是语言特性。6. 双包实例风险(Dual-Package Hazard) — 当
"import" 和 "require" 指向不同构建文件时,同一个类会被实例化为两个不同的构造函数,instanceof 会返回 false,共享状态(如计数器、注册表)也会分裂成两份。7. 发布库的最佳实践 — 用
.mjs/.cjs 明确文件格式;package.json 中 "type" 字段决定 .js 的解析方式;相对 ESM 导入必须带文件扩展名;用 "exports" 条件导出为不同系统提供入口,但要警惕实例分裂。8. 调试模块问题的检查清单 — 从导入文件的格式、解析到的入口点、返回值形状、本地变量持有的是绑定还是副本、评估是否完成、是否存在循环依赖、模块身份是否一致、是否经过构建工具转换、是否通过打包后的 tarball 测试等 9 个维度系统排查。
9. 九条实践规则 — 新项目优先用 ESM;库优先用命名导出;可变导出视为共享进程状态;CJS 属性会变时保留对象而非解构;移除循环依赖而非绕开;条件/延迟加载用动态
import();包边界显式声明文件格式;假设不同导出目标是不同实例;通过包名而非源码路径测试。URL:
https://blog.gaborkoos.com/posts/2026-08-14-Your-Modules-Are-Lying-to-You/
《npm 上 ESM 与 CJS 的占比变迁数据》
标签:#JavaScript #NodeJS #ESM #CommonJS #npm #模块系统
总结:
该项目持续追踪 npm 高影响力(热门)包中 ESM、CJS、Dual(双模式)及 Faux(伪 ESM)的占比变化。数据显示,纯 ESM 包从 2021 年 8 月的 6% 增长至 2026 年 6 月的 16%;Dual 模式包增长最为迅猛(从 1.7% 到 22%),成为主流过渡方案;而纯 CJS 虽仍占最大份额(52%),但占比持续下降。2025 年底样本量骤增,反映 npm 生态整体扩张。
文章要点:
1. 纯 ESM 稳步增长但尚未过半**:从 2021 年 341 个包(6.1%)增至 2026 年 2590 个(16.0%),五年增长约 2.6 倍,但仍是少数派
2. **Dual 模式成为最大赢家**:从 95 个(1.7%)暴涨至 3574 个(22.0%),说明"同时支持 ESM 和 CJS"已成为库作者最务实的选择
3. **纯 CJS 仍是主流但份额萎缩**:从 77.4% 降至 51.6%,虽然绝对数量增加,但相对占比持续被侵蚀
4. "伪 ESM"(Faux)长期僵持**:表面用 ESM 语法但实际编译为 CJS 的包,占比一直维持在 10% 左右,说明彻底转型仍有阻力
5. **生态规模急剧扩张**:样本包总数从 5617 个增至 16231 个,2025 年底几乎翻倍,反映 npm 整体生态的蓬勃发展
6. **数据方法论透明**:基于
URL:
https://github.com/wooorm/npm-esm-vs-cjs#data
标签:#JavaScript #NodeJS #ESM #CommonJS #npm #模块系统
总结:
该项目持续追踪 npm 高影响力(热门)包中 ESM、CJS、Dual(双模式)及 Faux(伪 ESM)的占比变化。数据显示,纯 ESM 包从 2021 年 8 月的 6% 增长至 2026 年 6 月的 16%;Dual 模式包增长最为迅猛(从 1.7% 到 22%),成为主流过渡方案;而纯 CJS 虽仍占最大份额(52%),但占比持续下降。2025 年底样本量骤增,反映 npm 生态整体扩张。
文章要点:
1. 纯 ESM 稳步增长但尚未过半**:从 2021 年 341 个包(6.1%)增至 2026 年 2590 个(16.0%),五年增长约 2.6 倍,但仍是少数派
2. **Dual 模式成为最大赢家**:从 95 个(1.7%)暴涨至 3574 个(22.0%),说明"同时支持 ESM 和 CJS"已成为库作者最务实的选择
3. **纯 CJS 仍是主流但份额萎缩**:从 77.4% 降至 51.6%,虽然绝对数量增加,但相对占比持续被侵蚀
4. "伪 ESM"(Faux)长期僵持**:表面用 ESM 语法但实际编译为 CJS 的包,占比一直维持在 10% 左右,说明彻底转型仍有阻力
5. **生态规模急剧扩张**:样本包总数从 5617 个增至 16231 个,2025 年底几乎翻倍,反映 npm 整体生态的蓬勃发展
6. **数据方法论透明**:基于
npm-high-impact 热门包分析,爬取 package.json 的 latest 版本,约 15 分钟完成,结果以 CSV 和 SVG 可视化呈现URL:
https://github.com/wooorm/npm-esm-vs-cjs#data