模块化简介
模块化编程是一种将代码分割成独立的、可重用的部分的方法。每个模块都有自己的作用域,并且可以导出一些功能供其他模块使用,同时也可以导入其他模块的功能。JavaScript 中主要有两种模块系统:CommonJS 和 ES6 Modules(ESM) 。
ESM 是静态模块系统,在编译/构建阶段就能确定哪些 export 被使用了,因此可以进行 Tree Shaking,把没用到的代码“摇掉”,最终打包体积更小,加载更快。
CommonJS 是动态模块系统,require() 是运行时函数调用,构建工具无法在编译时确定依赖关系,所以即使某个函数没被使用,整个模块依然会被完整打包进去。
1. CommonJS
CommonJS 是一种用于服务器端 JavaScript 的模块系统,主要用于 Node.js 环境。它通过 require 函数来导入模块,并使用 module.exports 或 exports 来导出内容。
导出单个函数/变量:
当你只需要从一个模块中导出一个单一的函数或变量时,可以使用 module.exports 直接导出该函数或变量。
// math.js
function add(a, b) {
return a + b;
}
module.exports = add; // 直接导出一个函数
导出多个函数/变量:
如果你需要从一个模块中导出多个函数或变量,可以将它们放在一个对象中,然后使用 module.exports 导出整个对象。
// math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
module.exports = {
add: add,
subtract: subtract
};
// 或者简写为:
module.exports = { add, subtract };
导入模块
解构导入:
解构导入允许你从模块中提取特定的函数或变量,并直接赋值给新的变量名。这种方式使得代码更加简洁和易读。
const { add, subtract } = require('./math');
console.log(add(2, 3)); // 输出 5
console.log(subtract(5, 2)); // 输出 3
在这个例子中,我们从 math.js 模块中解构导入了 add 和 subtract 函数,并可以直接使用它们。
整体导入:
整体导入意味着将整个模块作为一个对象导入。你可以通过这个对象访问模块中导出的所有函数和变量。
const math = require('./math');
console.log(math.add(2, 3)); // 输出 5
console.log(math.subtract(5, 2)); // 输出 3
在这个例子中,我们将 math.js 模块整体导入到 math 变量中,然后通过 math.add 和 math.subtract 访问其中的函数。
三、ES6 Modules (ESM)
ES6 Modules 是现代 JavaScript 的标准模块系统,支持在浏览器和服务器端使用。它通过 import 和 export 来实现模块的导入和导
导出模块
命名导出:
命名导出允许你从一个模块中导出多个函数、变量或类。每个导出都有一个名称,并且在导入时必须使用相同的名称(除非重命名)。
// math.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
默认导出:
默认导出允许你从一个模块中导出一个主要的值或函数。每个模块只能有一个默认导出,并且在导入时不需要使用花括号。
// math.js
export default function multiply(a, b) {
return a * b;
}
或者使用箭头函数:
const multiply = (a, b) => a * b;
export default multiply;
导入模块
命名导入:
使用 import { ... } from '...' 语法可以从模块中导入命名导出的内容
import { add, subtract } from './math.js';
console.log(add(2, 3)); // 输出 5
console.log(subtract(5, 2)); // 输出 3
默认导入:
使用 import ... from '...' 语法可以从模块中导入默认导出的内容。不需要使用花括号。
import multiply from './math.js';
console.log(multiply(2, 3)); // 输出 6
混合导入:
可以同时导入默认导出和命名导出的内容。但是注意,默认导出必须写在前面,不然会报错。
import multiply, { add, subtract } from './math.js';
console.log(multiply(2, 3)); // 输出 6
console.log(add(2, 3)); // 输出 5
console.log(subtract(5, 2)); // 输出 3
重命名导入:
import { add as sumFunction } from './math.js';
console.log(sumFunction(2, 3)); // 输出 5
导入所有内容为对象:
使用 * as 语法可以将整个模块导入为一个对象。
import * as math from './math.js';
console.log(math.add(2, 3)); // 输出 5
console.log(math.subtract(5, 2)); // 输出 3
export与export default
Tree-shaking 只对 export(命名导出)有效,而对 export default 导出的对象无效,是因为 default 导出的是一个整体值(比如对象),无法静态分析其中哪些属性被用到了;而命名导出是“粒度清晰的绑定”,构建工具可以精确地知道哪个导出没被使用,从而剔除。
四、对比 CommonJS 和 ES6 Modules
| 特性 | CommonJS | ES6 Modules |
|---|---|---|
| 语法 | require() / module.exports | import / export |
| 导入时机 | 运行时加载 | 编译时加载(静态分析) |
| 默认导出 | 不支持,默认需要自己模拟 | 支持 |
| 命名导出 | 需要手动打包到对象中 | 直接支持 |
| 动态导入 | 支持(如 require()) | 支持(如 import()) |
五、总结
- CommonJS 更适合于 Node.js 环境,提供了灵活的动态导入机制。
- ES6 Modules 提供了更清晰、更现代化的语法,支持静态分析,适用于现代前端开发框架(如 React、Vue)以及现代浏览器环境。