前言
在前端开发中,我们在很多地方都听过模块化开发。css有模块化,js也有模块化,在我们经常使用到的vue.js和react中也有模块化。这篇文章主要介绍什么是模块化以及node.js中的模块化。
模块化
模块化就是将一个复杂的程序按照一定规范封装成几个块。并组合在一起。那么这一个块就称之为模块。模块的内部数据和实现都是私有的,只是向外部暴露一些接口与外部其他模块进行通信。
作用
- 避免命名冲突
- 降低耦合,从而实现按需加载
- 高复用性
- 高维护性
模块化规范
模块化是需要依据一定的规范进行的,不是随心所欲地就完成了,这个规范也就是模块化规范。常用的模块化规范根据服务端和浏览器端规范可以分为以下几种
1. CommonJS规范--服务端规范
CommonJS规范是服务端规范,也是Node.js使用的模块化规范,是这篇文章重点要介绍的内容。
1.1 规范内容
CommonJS规范规定:
- 每个文件就是一个模块,有自己的作用域
- 每个模块内部,module变量就代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口对象。也就是说加载某个模块,其实是加载该模块的 module.exports 对象。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了。
- 模块加载的顺序,按照其在代码中出现的顺序。在服务器端模块的加载时运行时同步加载的。在浏览器端模块需要提前编译打包处理。
1.2 模块的暴露
模块的暴露是通过exports关键字来实现的。在Node.js中,每个模块都有一个exports接口对象,可以把公共的变量和方法挂载到这个对象上,这样就能实现模块的暴露了。
模块的暴露有两种方式:exports和module.exports
exports
语法
exports.key = value;
相当于是给exports对象添加一个key属性,值为value,value为任意数据类型。
示例
// module1.js
const name = '张三';
const age = 18;
const sayHi = () => {
console.log('你好,我是张三');
};
exports.name = name;
exports.age = age;
exports.sayHi = sayHi;
module.exports
语法
module.exports = obj;
module.exports.key = value;
第一种是直接暴露一个默认对象,没有对象名,内容是obj的内容。 第二种是给exports对象添加一个key属性,值为value,value为任意数据类型。
示例
// module2.js
module.exports = {
name: '张三',
age: 18,
sayHi: () => {
console.log('你好,我是张三');
}
}
// module3.js
const name = '张三';
const age = 18;
const sayHi = () => {
console.log('你好,我是张三');
};
module.exports.name = name;
module.exports.age = age;
module.exports.sayHi = sayHi;
两者的区别
模块暴露的本质是exports对象。Node.js 中每个模块都有一个 module 对象,module 对象中的有一个 exports 属性称之为接口对象。我们需要把模块之间公共的方法或属性挂载在这个接口对象中,方便其他的模块使用。
exports和module.exports的区别有以下几点:
- 使用exports时,只能单个设置属性exports.name = name;
- 使用module.exports时,即可以单个设置属性module.exports.name = name;也可以整个赋值module.exports = {}
- Node中每个模块的最后,都会执行
return: module.exports。 - Node中每个模块都会把
module.exports指向的对象赋值给一个变量exports,也就是说exports = module.exports。 module.exports = XXX,表示当前模块导出一个单一成员,结果就是XXX。- 如果需要导出多个成员,则必须使用
exports.add = XXX; exports.foo = XXX。或者使用module.exports.add = XXX; module.export.foo = XXX。
1.3 模块的引入
既然有模块的导出,那必须有模块的引入。模块的引入是用require函数来实现的。
语法
const module1 = require('module1');
module1表示的是模块名,在不同的情况下表示不同的内容:
- 内置模块: require的是包名
- 第三方模块:require的是包名
- 自定义模块:require的是文件路径,可以是相对路径也可以是绝对路径
示例
// 引入上述module1.js
const module1 = require('./module1');
console.log('module1', module1);
require函数的作用:
- 执行导入模块中的代码
- 返回导入模块中的接口对象exports
模块的初始化
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了。
const module1 = require('./module1');
console.log('module1', module1);
module1.name = '李四';
const module2 = require('./module1');
console.log('module2', module1);
效果如下:
2. AMD规范--浏览器端规范
AMD规范是RequireJS在推广过程中对模块化定义的规范化产出,它主要具有以下几个特点:
- 异步加载模块
- 依赖前置、提前执行:也就是说把所有的依赖都引入成功完再执行代码
- define定义模块
3. CMD规范--浏览器端规范
CMD规范是SeaJS在推广过程中对模块化定义的规范化产出,它主要具有以下几个特点:
- 同步加载模块
- 依赖就近、延迟执行:也就是说执行到对应代码时再去加载对应的依赖
- define定义模块,export导出
4. ES6规范--浏览器端规范
ES6规范就是我们常用到的import导入和export导出