node.js之模块化

198 阅读4分钟

前言

在前端开发中,我们在很多地方都听过模块化开发。css有模块化,js也有模块化,在我们经常使用到的vue.js和react中也有模块化。这篇文章主要介绍什么是模块化以及node.js中的模块化。

模块化

模块化就是将一个复杂的程序按照一定规范封装成几个块。并组合在一起。那么这一个块就称之为模块。模块的内部数据和实现都是私有的,只是向外部暴露一些接口与外部其他模块进行通信。

作用

  • 避免命名冲突
  • 降低耦合,从而实现按需加载
  • 高复用性
  • 高维护性

模块化规范

模块化是需要依据一定的规范进行的,不是随心所欲地就完成了,这个规范也就是模块化规范。常用的模块化规范根据服务端和浏览器端规范可以分为以下几种

1. CommonJS规范--服务端规范

CommonJS规范是服务端规范,也是Node.js使用的模块化规范,是这篇文章重点要介绍的内容。

1.1 规范内容

CommonJS规范规定:

  1. 每个文件就是一个模块,有自己的作用域
  2. 每个模块内部,module变量就代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口对象。也就是说加载某个模块,其实是加载该模块的 module.exports 对象。
  3. 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了。
  4. 模块加载的顺序,按照其在代码中出现的顺序。在服务器端模块的加载时运行时同步加载的。在浏览器端模块需要提前编译打包处理。

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的区别有以下几点:

  1. 使用exports时,只能单个设置属性exports.name = name;
  2. 使用module.exports时,即可以单个设置属性module.exports.name = name;也可以整个赋值module.exports = {}
  3. Node中每个模块的最后,都会执行 return: module.exports
  4. Node中每个模块都会把 module.exports指向的对象赋值给一个变量 exports,也就是说 exports = module.exports
  5. module.exports = XXX,表示当前模块导出一个单一成员,结果就是XXX。
  6. 如果需要导出多个成员,则必须使用 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);

1681310468758.png

require函数的作用:

  • 执行导入模块中的代码
  • 返回导入模块中的接口对象exports

模块的初始化

模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了。

const module1 = require('./module1');
console.log('module1', module1);
module1.name = '李四';
const module2 = require('./module1');
console.log('module2', module1);

效果如下:

1681311078357.png

2. AMD规范--浏览器端规范

AMD规范是RequireJS在推广过程中对模块化定义的规范化产出,它主要具有以下几个特点:

  • 异步加载模块
  • 依赖前置、提前执行:也就是说把所有的依赖都引入成功完再执行代码
  • define定义模块

3. CMD规范--浏览器端规范

CMD规范是SeaJS在推广过程中对模块化定义的规范化产出,它主要具有以下几个特点:

  • 同步加载模块
  • 依赖就近、延迟执行:也就是说执行到对应代码时再去加载对应的依赖
  • define定义模块,export导出

4. ES6规范--浏览器端规范

ES6规范就是我们常用到的import导入和export导出