什么是模块化
模块化
指为了解决一个复杂问题,自顶向下把系统逐层划分为若干个模块的过程。对于整个系统来说,模块是可以组合、分解和更换的单元。
代码进行模块化的好处:
- 提高了代码的复用性
- 增强了代码的可维护性
- 可以实现按需加载
模块化规范
模块化规范
就是对代码进行模块化
的拆分和组合时需遵从的规则。如:
- 引用模块的语法
- 在模块中向外暴露的语法格式
模块化规范的好处: 开发人员遵从同样的模块化编写代码,降低了沟通的成本,极大的方便了各个模块间的相互调用。
Node.js 模块化
为了让Node.js的文件可以相互调用,Node.js
提供了一个简单的模块系统。模块是Node.js
应用程序的基本组成部分,文件和模块是一一对应的,即一个Node.js
对应一个模块。每个文件都有自己的作用域,在一个文件中定义的变量、函数、类都是私有的,对其他文件不可见,这样可以防止全局变量被污染。
Node.js
应用由模块组成,模块遵从CommonJS规范。CommonJS规范规定,每个模块内部,module
变量代表当前模块,其为一个对象,它的exports
属性(module.exports)向外暴露。加载某个模块,其实就是加载该模块的module.exports
的值。
var x = 1
var add = function(a,b){
return a + b
}
// 暴露
module.exports.x = x
module.exports.add = add
// 引入模块
var xxx = require("xxx")
console.log(xxx.x) // 1
console.log(xxx.add(1,2)) // 3
CommonJS
模块特点:
- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,但只会在第一次加载时运行一次。第一次完成加载后即被缓存
- 模块加载的顺序按照代码编写的顺序加载
分类
Node.js
中根据模块来源不同,将模块分为三大类:
- 内置模块 内置模块由Node.js官方提供,如:fs、http、path
- 自定义模块 开发人员编写的js文件
- 第三方模块 由第三方开发出来的模块,使用前需先下载
共享
每个模块内部都有一个module
对象,其 存储了当前模块的有关信息。
/**
{
id: '.',
path: 'D:\\studyspace\\code\\node\\node_001',
exports: {},
filename: 'D:\\studyspace\\code\\node\\node_001\\index.js',
loaded: false,
children: [],
paths: [
'D:\\studyspace\\code\\node\\node_001\\node_modules',
'D:\\studyspace\\code\\node\\node_modules',
'D:\\studyspace\\code\\node_modules',
'D:\\studyspace\\node_modules',
'D:\\node_modules'
]
}
*/
console.log(module)
在自定义模块中,可以使用module.exports
或exports
将模块中的成员、方法对外暴露,供外界使用。(使用require()
引入模块时,得到用于是module.exports
指向的对象)
console.log(module.exports) // {}
const age = 10
// 向 module.exports 对象上挂载 age 属性
module.exports.age = age
// 向 module.exports 对象上挂载 name 属性
module.exports.name = '张三'
// 向 module.exports 对象上挂载 sayHi 方法
module.export.sayHi = function(){
console.log('Hi, Node.js')
}
注意:exports
不能直接指向一个值
加载
使用require()
即可加载需要的模块。require
命令的基本功能是,读入并执行一个JS文件,然后返回该模块的exports
对象。
// 加载内置模块
const fs = require('fx')
// 加载自定义模块(可以省略 .js 的后缀名)
const xxx = require('./xxx.js')
// 加载第三方模块(使用前需下载该模块)
const moment = require('moment')
注意:
模块第一次加载后会被缓存,即多次调用require()
引入同一模块,不会导致该模块中的代码被执行多次,提高了模块加载效率。
模块的加载机制
Node.js
加载模块流程图
内置模块的加载机制
内置模块是由Nolde.js
官方提供的模块,内置模块
的加载优先级最高
自定义模块的加载机制
加载自定义模块时,必须指定以./
或/
开头的路径标识符,否则Node.js
会将其当做内置模块或第三方模块。
若在引入模块时,省略了.js
扩展名,则Node.js
会按以下顺序加载文件(找到即返回,最终没有找到就报错):
- 按照
确切的文件名
进行加载 - 补全
.js
扩展名进行加载 - 补全
.json
扩展名进行加载 - 补全
.node
扩展名进行加载
第三方模块的加载机制
加载第三方模块,模块路径无须以./
或/
开头。Node.js
会从当前模块的父目录开始,尝试从其node_modules
文件夹中加载其模块;若没有找到则移动到再上一层的node_modules
文件夹中加载,其次类推,直到找到为止。
目录作为模块
当把目录作为模块标识符,传递给**require()**进行加载时,有三种加载方式:
- 在被加载的目录下找到一个
package.json
的文件,并寻找main
属性,其值作为require()
加载的入口 - 若目录下没有
package.json
文件,或main
入口不存在或无法解析,则Node.js
尝试加载目录下的index.js
文件 - 若以上两步都失败了,则抛出
Error: Cannot find module 'xxx'