欢迎使用我的小程序👇👇👇👇

一、为什么我们需要模块化?
想象一下,你正在建造一座乐高城堡。如果所有的积木都混在一个大袋子里,每次要找特定零件都得翻遍整个袋子,这会是多么低效!早期的JavaScript开发就是这样------所有代码都写在一个或几个文件中,导致:
- 变量污染全局作用域
- 难以维护和调试
- 代码复用困难
- 依赖关系混乱
模块化就像把乐高积木按颜色、形状分类放在不同的盒子里,让搭建变得更有序、更高效。
二、CommonJS:Node.js的"老将"
基本概念
CommonJS是Node.js默认的模块系统,它采用同步加载的方式,非常适合服务器端环境。
核心语法
javascript
// 导出模块 - math.js
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
// 方式1:导出单个对象
module.exports = { add, multiply };
// 方式2:逐个添加属性
exports.add = add;
exports.multiply = multiply;
// 导入模块 - app.js
const math = require('./math.js');
console.log(math.add(2, 3)); // 5
特点解析
同步加载:像在图书馆借书,必须等上一本书拿到手,才能借下一本
javascript
// 模块会立即执行
const fs = require('fs'); // 阻塞执行,直到fs模块完全加载
const data = fs.readFileSync('file.txt'); // 继续阻塞
动态导入:你可以在条件语句中导入模块
javascript
if (userNeedsAdvancedFeature) {
const advancedModule = require('./advanced');
// 使用模块
}
适用场景:
- Node.js后端开发
- 构建工具(如Webpack、Browserify转换后可在浏览器使用)
三、ES Module:现代JavaScript的"新星"
基本概念
ES Module是ECMAScript 2015(ES6)引入的官方模块标准,采用异步加载,是现代前端开发的首选。
核心语法
javascript
// 导出模块 - math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
// 默认导出(每个模块只能有一个)
const defaultFunction = () => console.log('默认导出');
export default defaultFunction;
// 导入模块 - app.js
import { add, multiply } from './math.js';
import myDefault from './math.js'; // 导入默认导出
// 重命名导入
import { add as sum } from './math.js';
// 整体导入
import * as math from './math.js';
特点解析
静态分析:像餐厅点餐,先看完整菜单再决定点什么
javascript
// 导入声明必须在顶层,不能在条件语句中
import { featureA } from './moduleA'; // ✓ 正确
if (condition) {
import { featureB } from './moduleB'; // ✗ 错误!语法不允许
}
// 但可以使用动态导入函数
if (condition) {
import('./moduleB').then(module => {
// 使用module.featureB
});
}
异步加载:多个模块可以并行加载
javascript
// 模块1和模块2可以同时加载
import { utils } from './utils.js';
import { api } from './api.js';
// 动态导入返回Promise
const loadModule = async () => {
const module = await import('./dynamic-module.js');
module.doSomething();
};
实时绑定:导入的是值的引用,而不是副本
javascript
// counter.js
export let count = 0;
export const increment = () => { count++; };
// app.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1(值被更新了!)
四、CommonJS vs ES Module:直接对比
| 特性 | CommonJS | ES Module |
|---|---|---|
| 语法 | require() / module.exports |
import / export |
| 加载时机 | 运行时加载 | 编译时静态解析 |
| 加载方式 | 同步 | 异步 |
| 环境 | 主要为Node.js | 浏览器和Node.js |
| 值类型 | 值的拷贝 | 值的引用(实时绑定) |
| 条件导入 | 支持 | 静态导入不支持,动态导入支持 |
| 循环依赖 | 支持但可能有问题 | 支持且更可靠 |
| 严格模式 | 默认非严格模式 | 默认严格模式 |
循环依赖示例对比
CommonJS的问题:
javascript
// a.js
exports.loaded = false;
const b = require('./b.js');
console.log('在a中,b是', b);
exports.loaded = true;
// b.js
exports.loaded = false;
const a = require('./a.js'); // 此时a还没有完全加载
console.log('在b中,a是', a); // a.loaded为false
exports.loaded = true;
ES Module的处理:
javascript
// a.js
import { bLoaded } from './b.js';
export let aLoaded = false;
aLoaded = true;
// b.js
import { aLoaded } from './a.js';
export let bLoaded = false;
bLoaded = true;
// 可以正常工作,但需要注意初始化顺序
五、在现代项目中如何使用?
Node.js中的使用
Node.js从v13.2.0开始稳定支持ES Module:
-
使用
.mjs扩展名javascript// module.mjs export const hello = () => "Hello ES Module!"; // app.mjs import { hello } from './module.mjs'; -
在
package.json中设置"type": "module"json{ "name": "my-app", "type": "module", "scripts": { "start": "node app.js" } }
浏览器中的使用
html
<!-- 直接使用ES Module -->
<script type="module">
import { createApp } from './app.js';
createApp();
</script>
<!-- 支持相对和绝对路径 -->
<script type="module" src="./modules/main.js"></script>
互操作性
在ES Module中导入CommonJS模块:
javascript
// 可以导入CommonJS模块
import commonJSModule from './commonjs-module.cjs';
import { namedExport } from './commonjs-module.cjs'; // 如果CommonJS模块有提供
// 在package.json中指定不同扩展名的处理方式
六、实践建议
-
新项目:优先使用ES Module,它是未来的标准
-
Node.js项目 :
- 新项目:使用ES Module(设置
"type": "module") - 现有项目:逐步迁移或维持CommonJS
- 新项目:使用ES Module(设置
-
浏览器项目:使用ES Module,配合构建工具(如Webpack、Vite)
-
库/包开发 :考虑双模式支持
json// package.json { "name": "my-library", "main": "./dist/commonjs/index.js", "module": "./dist/esm/index.js", "exports": { "require": "./dist/commonjs/index.js", "import": "./dist/esm/index.js" } }
七、常见问题解答
Q:我应该学习哪一个? A:两者都要了解!ES Module是未来,但很多现有项目使用CommonJS。
Q:可以在同一个文件中混用吗? A:尽量避免,但在Node.js中可以通过动态导入互相调用。
Q:哪个性能更好? A:ES Module的静态特性允许更好的优化和摇树(tree-shaking)。
总结
模块化让JavaScript从"玩具语言"成长为"工程语言"。CommonJS像是一位经验丰富的老兵,在Node.js生态中建立了坚实基础;ES Module则像是一位充满活力的新星,代表着JavaScript的未来方向。
记住一个简单的选择策略:
- 前端开发 → ES Module
- Node.js新项目 → ES Module
- Node.js旧项目维护 → CommonJS
- 通用库开发 → 考虑双模式支持
无论选择哪种方式,模块化的核心思想都是一致的:关注点分离、高内聚低耦合、代码复用。掌握了这些概念,你就掌握了现代JavaScript开发的钥匙。
模块化不是目的,而是手段。真正的目标是编写可维护、可扩展、高质量的代码。