全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-16 8 分钟 ✍️ juanwangdev

JavaScript 模块化规范(CommonJS、ES Module)

模块化将代码分割为独立单元,CommonJS 是 Node.js 传统规范,ES Module 是 ES6 标准规范,两者各有特点。

CommonJS 规范

JavaScript
// 导出模块
// 方式一:module.exports
module.exports = {
    name: 'utils',
    add: (a, b) => a + b,
    subtract: (a, b) => a - b
};

// 方式二:exports(是module.exports 的引用)
exports.multiply = (a, b) => a * b;
exports.divide = (a, b) => a / b;

// 注意:不能直接替换 exports
exports = { name: 'error' };  // 错误!不会影响 module.exports

// 导入模块
const utils = require('./utils');
console.log(utils.add(1, 2));  // 3

// 解构导入
const { add, subtract } = require('./utils');

// 导入核心模块
const fs = require('fs');
const path = require('path');

// 导入 npm 包
const express = require('express');
const lodash = require('lodash');

CommonJS 特性

JavaScript
// 1. 同步加载(适合服务器端)
const utils = require('./utils');  // 同步读取文件
console.log('加载完成');

// 2. 动态加载(运行时决定)
const moduleName = process.env.MODULE;
const module = require(`./modules/${moduleName}`);  // 动态路径

// 3. 输出值拷贝
// counter.js
let count = 0;
module.exports = {
    getCount: () => count,
    increment: () => count++
};

// main.js
const counter = require('./counter');
console.log(counter.getCount());  // 0
counter.increment();
console.log(counter.getCount());  // 1
// 原模块的 count 变化,但导出的 getCount 函数能访问到

// 4. 单例模式(缓存)
// 多次 require 同一模块返回同一对象
const a = require('./utils');
const b = require('./utils');
console.log(a === b);  // true(模块缓存)

ES Module 规范

JavaScript
// 导出模块
// 方式一:命名导出
export const name = 'utils';
export function add(a, b) {
    return a + b;
}
export function subtract(a, b) {
    return a - b;
}

// 方式二:集中导出
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
export { multiply, divide };

// 方式三:默认导出
export default {
    name: 'utils',
    add: (a, b) => a + b
};

// 方式四:混合导出
export default class Calculator { }
export const helper = () => {};

// 导入模块
// 命名导入
import { add, subtract } from './utils.js';
console.log(add(1, 2));  // 3

// 默认导入
import Calculator from './utils.js';

// 混合导入
import Calculator, { helper } from './utils.js';

// 全部导入
import * as utils from './utils.js';
console.log(utils.add(1, 2));

// 重命名导入
import { add as sum } from './utils.js';
console.log(sum(1, 2));

ES Module 特性

JavaScript
// 1. 异步加载(适合浏览器)
import { add } from './utils.js';  // 异步加载
// 代码继续执行,模块加载完成后执行导入

// 2. 静态加载(编译时确定)
import { add } from './utils.js';  // import 必须在顶层
// 不能动态导入:import(`./${module}.js`) // SyntaxError

// 动态导入使用 import()
const moduleName = 'utils';
import(`./modules/${moduleName}.js`).then(module => {
    module.add(1, 2);
});

// 3. 输出值引用(绑定)
// counter.js
export let count = 0;
export function increment() {
    count++;
}

// main.js
import { count, increment } from './counter.js';
console.log(count);  // 0
increment();
console.log(count);  // 1(实时更新!)
// ES Module 导出的是值的引用,不是拷贝

// 4. 严格模式
// ES Module 自动启用严格模式,无需 'use strict'
export const obj = {};  // this === undefined

CommonJS vs ES Module 对比

特性CommonJSES Module
加载时机运行时加载编译时静态分析
加载方式同步异步
输出方式值拷贝值引用(实时绑定)
this指向当前模块undefined
动态导入require(path)import(path)返回Promise
严格模式需手动开启自动开启
文件扩展名可省略必须完整(浏览器)
循环依赖只输出已执行部分动态引用

循环依赖处理

JavaScript
// CommonJS 循环依赖
// a.js
const b = require('./b');
console.log('a: b.value =', b.value);
module.exports.value = 'a';

// b.js
const a = require('./a');  // a 模块部分执行,exports 为 {}
console.log('b: a.value =', a.value);  // undefined
module.exports.value = 'b';

// 执行顺序:
// a 开始执行 → require b → b 开始执行 → require a → a 已加载,返回部分 exports
// b 输出 undefined → b 完成 → a 继续 → a 输出 'b'

// ES Module 循环依赖
// a.mjs
import { value } from './b.mjs';
console.log('a: b.value =', value);  // 'b'
export const value = 'a';

// b.mjs
import { value } from './a.mjs';
console.log('b: a.value =', value);  // undefined(引用,未执行到)
export const value = 'b';

// ES Module 使用动态引用,访问时才取值
// 如 b.mjs 延迟访问 a.value,会得到 'a'

Node.js 使用 ES Module

JavaScript
// 方式一:.mjs 文件扩展名
// utils.mjs
export const add = (a, b) => a + b;

// 方式二:package.json type: "module"
{
    "type": "module"
}
// 所有 .js 文件作为 ES Module

// 方式三:--experimental-modules(旧版本)
node --experimental-modules index.mjs

// Node.js 内置模块 ES Module 导入
import fs from 'fs';
import { readFileSync } from 'fs';
import fsPromises from 'fs/promises';  // Promise 版本

// CommonJS 与 ES Module 混用限制
// ES Module 中不能直接 require
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const cjsModule = require('./cjs-module.cjs');

浏览器使用 ES Module

HTML
<!-- script type="module" -->
<script type="module">
    import { add } from './utils.js';
    console.log(add(1, 2));
</script>

<!-- 外部模块 -->
<script type="module" src="./app.js"></script>

<!-- 动态导入 -->
<script type="module">
    const module = await import('./utils.js');
    console.log(module.add(1, 2));
</script>

<!-- 注意:浏览器中路径必须完整 -->
import { add } from './utils.js';  // 正确
import { add } from './utils';     // 错误(缺少.js)

<!-- nomodule 兜底 -->
<script type="module" src="./app.js"></script>
<script nomodule src="./app-legacy.js"></script>

import() 动态导入

JavaScript
// 动态导入:返回 Promise
async function loadModule(name) {
    const module = await import(`./modules/${name}.js`);
    return module;
}

// 按需加载
button.addEventListener('click', async () => {
    const { showDialog } = await import('./dialog.js');
    showDialog();
});

// 懒加载路由
const routes = {
    home: () => import('./pages/Home.js'),
    about: () => import('./pages/About.js')
};

async function loadPage(page) {
    const module = await routes[page]();
    return module.default;
}

// 条件导入
if (featureEnabled) {
    const feature = await import('./feature.js');
    feature.init();
}

注意事项

  • ES Module 文件扩展名:Node.js 用 .mjstype: "module",浏览器必须完整路径
  • ES Module 自动严格模式,thisundefined
  • CommonJS 模块有缓存,多次 require 返回同一对象
  • ES Module 导出值引用,导入值会实时更新
  • import() 动态导入返回 Promise,可实现懒加载
JavaScript
// CommonJS 模块缓存
// utils.js
console.log('utils loaded');
module.exports = { name: 'utils' };

// main.js
require('./utils');  // 打印 'utils loaded'
require('./utils');  // 不打印(缓存)

// 清除缓存(开发调试用)
delete require.cache[require.resolve('./utils')];
require('./utils');  // 再次打印

// ES Module 无缓存机制,但浏览器会缓存 HTTP 请求
// Node.js ES Module 有内部缓存

要点总结

  • CommonJS:Node.js 模块规范,同步加载,值拷贝,运行时解析
  • ES Module:JavaScript 标准规范,异步加载,值引用,静态分析
  • CommonJS 导出:module.exportsexports,导入:require()
  • ES Module 导出:exportexport default,导入:import
  • CommonJS 可动态加载,ES Module 静态加载,import() 可动态
  • ES Module 导出值引用,导入值实时更新
  • 循环依赖:CommonJS 输出已执行部分,ES Module 动态引用
  • Node.js:.mjstype: "module" 启用 ES Module
  • 浏览器:<script type="module">,路径必须完整

📝 发现内容有误?点击此处直接编辑

← 上一篇 JavaScript 工程化基础(Babel、ESLint)
下一篇 → JavaScript 模块打包工具(Webpack、Rollup)
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库