.JavaScript模块化规范
1.模块化概述
1 什么事模块化?
模块化是指将一个复杂的程序按照一定的规范或设计思想,
拆分成若干个独立、可复用的模块,每个模块负责完成特定的功能,
并通过明确的接口与其他模块进行组合和交互。
2 为什么需要模块化?
在早期的前端开发中,代码往往写在一个全局作用域里,容易产生以下问题:
- 命名冲突:多个变量、函数同名,相互覆盖。
- 依赖混乱:文件之间的加载顺序必须手动保证,难以维护。
- 代码复用困难:复制粘贴代码导致重复和冗余。
- 难以测试:大段耦合代码难以单独测试。
3.模块化解决了这些问题,带来: - 高内聚、低耦合:每个模块专注自己的职责。
- 按需加载:只加载需要的模块,提升性能。
- 依赖管理清晰:通过 import / require 明确声明依赖关系。
- 易于维护和协作:多人可并行开发不同模块,代码结构清晰。
2.有哪些模块化规范?
1 commonJS -服务端应该广泛
2.AMD
3.CMD
4.ES 模块化浏览器应用广泛
3 commonJS 使用
3.1 初步体验
a.js
javascript
const name='张三'
function fn(){
return '你好'
}
exports.fn=fn; //导出
exports.name=name
b.js
javascript
// 注意:路径需要正确,如果是同一目录,应为 './a.js'
const a = require('./a.js');
console.log(a.name); // 输出:张三
console.log(a.fn()); // 输出:你好
require 的路径规则:
- 核心模块(如 fs、path):直接写模块名 require('fs')
- 自定义模块:使用相对路径 require('./a.js') 或 require('.../lib/a.js')
- 文件夹模块:如果 require 一个目录,会查找该目录下的 index.js
3.2 导出数据
在coomonjs标准中,导出数据有两种格式
第一种:module.exports = value
- value 可以是任意类型:对象、函数、字符串、类等。
- 会完全覆盖默认的导出对象,最终导出就是这个 value。
javascript
// person.js
module.exports = { name: '李四', age: 20 };
// 或者
module.exports = function() { console.log('hello'); };
第二种格式exports.name = value
- exports 是 module.exports 的一个引用。
- 通过给 exports 添加属性,本质上是给 module.exports 添加属性。
- 不能直接给 exports 赋值(如 exports = { a: 1 }),因为那会切断引用。
javascript
// utils.js
exports.add = (x, y) => x + y;
exports.sub = (x, y) => x - y;
// 等价于:
// module.exports.add = (x, y) => x + y;
// module.exports.sub = (x, y) => x - y;
3.3面试题
javascript
exports ={1:1}
exports.b=2
module.exports.c=3
module.exports ={d:4}
//合并之后是{d:4}
*无论如何修改对象,最后导出的格式都是module.exports
导出之后 也可以进行解析引入
3.4 导入(require)的细节
javascript
const someModule = require('./module.js');
require 的特性
- 同步加载:模块代码会按顺序执行,加载完成才继续。
- 缓存机制 :同一个模块多次 require,只会执行一次,后续返回缓存中的导出对象。
-加载非 JS 文件:可以通过自定义扩展名加载 JSON、Node 插件等。例如 require('./data.json') 会自动解析为对象
解构导入
javascript
const { fn, name } = require('./a.js');
console.log(fn(), name);
3.5 注意事项
不要混用两种导出方式时产生覆盖
javascript
exports.a = 1;
module.exports = { b: 2 }; // 前面的 exports.a 会被覆盖,最终只导出 { b: 2 }
循环依赖:CommonJS 遇到循环依赖时,会输出当前已执行部分的内容(可能不完整),需谨慎设计。
文件扩展名可省略:require('./a') 会依次尝试 .js、.json、.node。
在 ES Module 中使用 CommonJS:可通过 import 或 createRequire 实现,但不推荐混合。
3.6 小结
导出方式 语法 最终导出内容
默认导出对象 module.exports = obj obj
添加属性 exports.prop = value module.exports 对象上增加属性
直接给 exports 赋值 exports = something ❌ 无效,导出的仍是原 module.exports
记忆口诀:
导出看 module.exports,
exports 只是小别名。
若要覆盖直接赋,
添加属性用点行。
4.ES6规范
ES6 模块化规范是官方标准,语言层面支持,浏览器和 Node.js 均可原生使用。它采用 import/export 语法,支持静态分析、树摇(tree shaking)、循环依赖处理优于 CommonJS。Node 环境中启用方式:使用 .mjs 后缀,或在 package.json 中设置 "type": "module"。
4.1 初步体验
a.js(导出模块)
javascript
// 命名导出
export const name = '张三';
export function fn() {
return '你好';
}
b.js(导入模块)
javascript
// 命名导入
import { name, fn } from './a.js';
console.log(name); // 张三
console.log(fn()); // 你好
注意:ES6 Module 默认使用严格模式,且必须在支持 ES Module 的环境中运行(浏览器
4.2 导出数据(export)
ES6 Module 提供了多种导出方式:
4.2.1 命名导出(Named Export)
可以导出多个变量、函数、类。
导出后必须通过相同的名称导入(可用 as 重命名)。
javascript
// 方式1:声明时直接导出
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export class Calculator { ... }
// 方式2:先声明,后统一导出
const a = 1;
const b = 2;
export { a, b };
// 方式3:重命名导出
const c = 3;
export { c as myC };
4.2.2 默认导出(Default Export)
- 每个模块只能有一个默认导出。
- 导入时可以使用任意名称。
javascript
// 方式1:直接导出匿名值
export default function() {
console.log('default function');
}
// 方式2:导出已命名的变量/函数/类
const myObj = { x: 10 };
export default myObj;
// 方式3:混合导出(命名 + 默认)
export const version = '1.0';
export default class Main { ... }
4.3 导入数据(import)
4.3.1 命名导入
javascript
import { name, age } from './person.js';
import { name as userName } from './person.js'; // 重命名
4.3.2 默认导入
javascript
import anyName from './module.js'; // 任意名称,对应 default 导出
4.3.3 混合导入
javascript
import defaultExport, { named1, named2 } from './module.js';
4.3.4 整体导入(命名空间导入)
javascript
import * as myModule from './module.js';
console.log(myModule.named1);
console.log(myModule.default); // 注意:default 需要 .default 访问
4.3.5 动态导入(运行时)
javascript
const module = await import('./some.js');
module.someMethod();
4.4 ES6 Module 的重要特性
1.静态结构:import/export 必须在模块顶层,不能写在条件语句或函数中。这支持了 Tree Shaking(打包时删除未使用的导出)。
2.实时绑定(Live Binding):导入的是导出值的引用,模块内部值变化会反映到导入方。
3.异步加载:适用于浏览器,支持
4.5问:CommonJS 和 ES Module 的本质区别是什么?
答:CommonJS 是运行时加载,模块是对象,输出的是值的拷贝。
ES Module 是编译时加载,模块是静态结构,输出的是值的只读引用,支持 Tree Shaking。
ES Module 的 import 会被提升到模块顶部,且不能在块级作用域中使用;CommonJS 的 require 可以在任何地方动态调用。