标准答案

  1. CommonJS 使用 require 和 module.exports,模块加载偏运行时,Node.js 早期生态大量使用。
  2. ESM 使用 import 和 export,语法是静态的,更适合 Tree Shaking、静态分析和现代浏览器加载。
  3. UMD 是兼容格式,通常同时兼容 CommonJS、AMD 和全局变量,早期库为了适配多环境会产出 UMD。
  4. IIFE 是立即执行函数表达式,常用于把变量包在函数作用域中,避免直接污染全局对象。
  5. 现代前端应用更偏向 ESM;库包为了兼容不同消费方,可能同时提供 ESM、CJS 和 UMD 产物。

题目解析

模块格式本质上是在回答两个问题:代码如何暴露能力,以及运行环境如何加载它。CommonJS 更像运行时拿模块,ESM 则把依赖关系写成编译期就能看懂的结构。

UMD 和 IIFE 的历史感更强。它们解决的是浏览器脚本标签、老构建工具、不同加载器之间的兼容问题,在现代应用源码里不会作为首选写法,但在库产物和遗留系统里仍然能见到。

理解这些格式有助于排查“为什么库不能被摇树优化”“为什么浏览器直接引入报错”“为什么一个包同时有 main、module、exports”。这些问题通常不是语法问题,而是产物格式和消费环境没有对齐。

代码示例

同一个能力可以用不同模块格式暴露出来。

JavaScript
// CommonJS
const utils = require('./utils')
module.exports = { format: utils.format }

// ESM
import { format } from './utils.js'
export { format }

// IIFE
;(function () {
  window.format = function format(value) {
    return String(value)
  }
})()

常见误区

  • 把 ESM 和 CommonJS 的差异只理解成 import 与 require 的写法不同。
  • 认为所有模块格式都同样适合 Tree Shaking。静态 ESM 通常更容易被构建工具分析。
  • 在浏览器里直接使用未打包的 CommonJS 代码,导致 require 或 module 未定义。

作者信息