node.js模块类型常见的分为三块:
内置模块:如fs、http模块等。。
用户自定义模块:用户自己编写的js文件
第三方模块:如通过npm包管理工具导入的第三方由node.js内置模块封装出来的,用以提高开发效率的包
使用require()方法,用以加载各种模块进行使用
// 加载内置文件系统模块fs
const fs = require('fs');
// 加载用户自定义模块
const ownModule = require('./ownModule.js');
// 加载通过npm安装的第三方日期模块dayjs
const dayjs = require('dayjs');
在每个用户自定义模块的.js文件中都有一个module对象,它里面存储了该模块有关的信息。
通过在自己创建的文件customModule.js中打印module对象输出如下信息:
PS C:\Users\林木木\Desktop\node\模块化\自定义模块> node .\customModule.js
Module {
id: '.',
path: 'C:\\Users\\林木木\\Desktop\\node\\模块化\\自定义模块',
exports: {},
filename: 'C:\\Users\\林木木\\Desktop\\node\\模块化\\自定义模块\\customModule.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\林木木\\Desktop\\node\\模块化\\自定义模块\\node_modules',
'C:\\Users\\林木木\\Desktop\\node\\模块化\\node_modules',
'C:\\Users\\林木木\\Desktop\\node\\node_modules',
'C:\\Users\\林木木\\Desktop\\node_modules',
'C:\\Users\\林木木\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
可见每个自定义模块的module对象中都包含id(模块标识符)、loaded(模块是否加载完成)、path(模块的搜索路径)等属性,我们将重点信息放在它的exports属性上
module的exports属性也是一个对象,我们可以通过module.exports对象,将自定义模块内的部分成员共享出去,供外界使用
在模块customModule.js中定义如下:
// 该成员和方法不打算共享出去,具有模块作用域,只能在该模块内访问,避免全局污染 let fruit = "Apple"; function sayName(f) { return f } sayName(fruit); // 该成员和方法将在后面共享出去 let hobby = 'basketball'; function sayHobby(h) { return h; } sayHobby(hobby); // 通过mudule.exports,向外共享成员hobby及方法sayHobby module.exports = { hobby, sayHobby }
'运行
在外部通过require()方法加载该模块:
const customModule = require('./customModule.js');
console.log(customModule); // { hobby: 'basketball', sayHobby: [Function: sayHobby] }
Node提供了exports对象,在默认情况下,exports和module.exports指向同一个对象
console.log(exports === module.exports); // true
'运行
But:最终共享的结果,以module.exports指向的对象为准
在模块exportsObj.js中定义如下:
// exports和module.exports指向同一个对象,最终以module.exports暴露的对象为准 exports.address = 'zzuli'; exports.sayAddress = function () { return this.address; } module.exports = { name: 'lmm' }
'运行
在外部通过require()方法加载该模块:
const exportObj = require('./exportsObj.js');
console.log(exportObj); // { name: 'lmm' }
若执意要在同一模块中同时使用module.exports和exports两个对象,共享模块内成员时,将有一下几种情况:
情况一:
// 向外共享 { gender: '男', age: '21'}; exports.username = 'lmm'; module.exports = { gender: '男', age: 21 }
'运行
解释:重写了module.exports对象,也就是module.exports指向了另外一个对象,而exports仍旧指向原来的对象,此时两者不指向同一个对象,根据最终共享的结果,以module.exports指向的对象为准,向外共享 { gender: ‘男’, age: ‘21’}
情况二:
// 向外共享 { address: 'zzuli'}; module.exports.address = 'zzuli'; exports = { height: '172', weight: '130' }
'运行
解释:重写了exports对象,也就是exports指向了另外一个对象,而module.exports仍旧指向原来的对象,此时两者不指向同一个对象,根据最终共享的结果,以module.exports指向的对象为准,向外共享 { address: ‘zzuli’}
情况三:
// 向外共享 { hobby: 'code', good: 'PingPong'}; exports.hobby = 'code'; module.exports.good = 'PingPong'
'运行
解释:此时两者都未重写,仍然指向同一个对象,根据最终共享的结果,以module.exports指向的对象为准(此时也包括exports指向的对象),向外共享{ hobby: ‘code’, good: ‘PingPong’}
情况四:
// 向外共享 { sport: 'basketball', fruit: 'Apple', drink: 'Cola'}; exports = { sport: 'basketball', fruit: 'Apple' } module.exports = exports; module.exports.drink = 'Cola';
'运行
解释:重写了exports对象后exports指向新的对象,但后续又修改了module.exports指向的对象为exports指向新的对象,因此二者仍然指向同一个对象,同理,根据最终共享的结果,以module.exports指向的对象为准(此时也包括exports指向的对象),向外共享{ sport: ‘basketball’, fruit: ‘Apple’, drink: ‘Cola’}
由此建议:不要再同一个模块中同时使用module.exports对象和exports对象
Node.js遵循了CommonJS模块化规范:规定了模块的特征和各模块之间如何相互依赖
CommonJS规定:
1.每个模块内部,module变量代表当前模块;
2.module变量是一个对象,它的exports属性(module.exports)是对外的接口;
3.通过require()加载某个模块,其实是加载该模块的module.exports属性