模块化是一种处理复杂系统分解成为更好的可管理模块的方式,模块化开发最终的目的是将程序划分成一个个小的结构
- 这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会影响到其他的结构
- 可以将自己希望暴露的变量、函数、对象等导出给其结构使用
- 可以通过某种方式,导入另外结构中的变量、函数、对象等
早期:全局函数模式 简单对象封装 IIFE
模块化的好处
- 避免命名冲突(减少命名空间污染)
- 更好的分离, 按需加载
- 更高复用性
- 高可维护性
CommonJs
CommonJS 是一种模块系统规范,主要用于在服务器端环境(如 Node.js)中管理模块。它提供了模块的定义、加载、导出机制,允许开发者在不同模块之间共享代码。在 Node.js 中,CommonJS 是默认的模块系统,虽然现在 Node.js 也支持 ECMAScript 模块,但 CommonJS 仍然广泛使用
CommonJS规范的核心变量:exports、module.exports、require,可以使用这些变量来方便的进行模块化开发
exports和module.exports可以负责对模块中的内容进行导出require函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容
exports导出
exports是一个对象,我们可以在这个对象中添加很多个属性,添加的属性会导出
// a.js
function add(num1, num2) {
return num1 + num2;
}
const message = "hello world";
console.log(exports); // {}
exports.add = add;
exports.message = message;
// main.js
// const { add, message } = require("./a"); // 可以拿到文件中导出的exports对象,相当于引用赋值
// console.log(add(10, 30)); // 40
// console.log(message); // hello world
const a = require("./a");
console.log(a.add(10, 30)); // 40
console.log(a.message); // hello world
module.exports导出
// b.js
function add(num1, num2) {
return num1 + num2;
}
const message = "hello world";
// 方式一
// module.exports.add = add;
// module.exports.message = message;
// console.log(module.exports === exports); // true
// 方式二:开发中常用,module.exports赋值新对象更灵活方便
module.exports = {
add,
message,
};
// main.js
const b = require("./b");
console.log(b.add(10, 20)); // 30
console.log(b.message); // hello world
require导入
require(X)
X是⼀个Node核⼼内置模块,⽐如path、http:直接返回核⼼模块,并且停⽌查找
X是以 ./ 或 ../ 或 /(根⽬录)开头的:
-
第⼀步:将
X当做⼀个⽂件在对应的⽬录下查找- 直接查找⽂件
X - 查找
X.js⽂件 - 查找
X.json⽂件 - 查找
X.node⽂件
- 直接查找⽂件
-
第⼆步:没有找到对应的⽂件,将
X作为⼀个⽬录:查找⽬录下⾯的index⽂件- 查找
X/index.js⽂件 - 查找
X/index.json⽂件 - 查找
X/index.node⽂件
- 查找
-
如果没有找到,那么报错:
not found
直接是⼀个X(没有路径),并且X不是⼀个核⼼模块:去**node_modules文件夹**中查找
CommonJS暴露的模块到底是什么? CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;
console.log(counter); // 3
incCounter();
console.log(counter); // 3
CommonJs特点
- 文件即模块,文件内所有代码都运行在独立的作用域,因此不会污染全局空间。
- 模块可以被多次引用、加载。在第一次被加载时,会被缓存,之后都从缓存中直接读取结果。
- 加载某个模块,就是引入该模块的 module.exports 属性。
- module.exports 属性输出的是值的拷贝,一旦这个值被输出,模块内再发生变化不会影响到输出的值。
- 模块加载顺序按照代码引入的顺序。
CommonJS加载模块是同步的,会阻塞
ES Module
export关键字
export关键字将一个模块中的变量、函数、类等导出
-
方式一:想导出谁就在语句声明的前面直接加上
export关键字 -
方式二:想导出谁则将需要导出的标识符,放到
export后面的{}中- 注意:这里的
{}里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象,export { message: message }是错误的写法;
- 注意:这里的
-
方式三:在方式二导出时给标识符起一个别名
// 方式一
export const message1 = "hello world1";
export function add1(num1, num2) {
return num1 + num2;
}
export class Person1 {
constructor(name) {
this.name = name;
}
}
// 方式二
const message2 = "hello world2";
function add2(num1, num2) {
return num1 + num2;
}
class Person2 {
constructor(name) {
this.name = name;
}
}
export { message2, add2, Person2 };
// 方式三
const message3 = "hello world3";
function add3(num1, num2) {
return num1 + num2;
}
class Person3 {
constructor(name) {
this.name = name;
}
}
export { message3, add3 as add0, Person3 as Person0 };
import关键字
import关键字负责从另外一个模块中导入内容
-
方式一:
import { 标识符列表 } from '模块'- 注意:这里的
{}也不是一个对象,里面只是存放导入的标识符列表内容
- 注意:这里的
-
方式二:通过
as关键字在导入时给标识符起别名 -
方式三:通过
* as 自己名字将模块功能放到一个模块功能对象上
// 结合export中的代码学习
import {
message1, // 方式一
message2,
message3,
add0 as add3, // 方式二
add1,
add2,
Person0 as Person3,
Person1,
Person2,
} from "./a.js";
import * as a from "./a.js"; // 方式三
console.log(
message1,
message2,
message3,
add1,
add2,
add3,
Person1,
Person2,
Person3,
a.message1,
a.message2,
a.message3,
a.add1,
a.add2,
a.add0,
a.Person1,
a.Person2,
a.Person0
);
export和import结合
/* util/index 通常是不编写逻辑的,在这里统一导入并导出 */
// 方式一
import {
message1,
message2,
message3,
add0 as add3,
add1,
add2,
Person0 as Person3,
Person1,
Person2,
} from "./a.js";
import { getData } from "./b.js";
export {
message1,
message2,
message3,
add3,
add1,
add2,
Person3,
Person1,
Person2,
getData,
};
// 方式二:结合
export {
message1,
message2,
message3,
add0 as add3,
add1,
add2,
Person0 as Person3,
Person1,
Person2,
} from "./a.js";
export { getData } from "./b.js";
// 方式三:建议当有相应的文档时再这样写
export * from "./a.js";
export * from "./b.js";
① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成
AMD规范(基本不用)
AMD是Asynchronous Module Definition`(异步模块定义)的缩写,⽤的是异步加载模块
CMD规范(基本不用)
CMD是Common Module Definition(通⽤模块定义)的缩写- ⽤的也是异步加载模块,但是它将
CommonJS的优点吸收了过来,但是⽬前CMD使⽤也⾮常少了 CMD也有⾃⼰⽐较优秀的实现⽅案:SeaJS