模块化
-
模块化解决的问题
- 代码的数量增多导致编写程序复杂度越来越高,此时如果依然将所有的代码编写到同一个文件中,代码就会变得非常难以维护,模块化就说解决这个问题的关键。
-
什么是模块?
-
模块简单理解就是一个代码片段,本来写在一起的JS代码,我们按照不同的功能将它拆分成一个个小的代码片段,这个代码片段就是一个模块,简单来说,就是化整为零。
-
模块简单理解就是一个代码片段,本来写在一起的JS代码,我们按照不同的功能将它拆分成一个个小的代码片段,这个代码片段就是一个模块,简单来说,就是化整为零。
-
模块化的好处
- 模块化后不再是所有的代码写在一起,而是按照功能做了不同的区分,当维护代码时可以比较快捷的找到那些要修改的代码,
- 模块化后,代码更容易被复用,同样一个模块可以在不同的项目中使用,大大降低开发成本
CommonJS
- 在node中,默认支持的模块化规范就叫CommonJS
- 模块就是一个JS文件,在模块内部任何变量或者其他对象都是私有的,不会暴露给外部模块,
- 在CommonsJS模块化规范中,在模块内部定义了一个module对象,module对象内部存储了当前模块的基本信息,同时module对象中有一个属性名exports,exports用来指定需要向外部暴露的内容,只需要将需要暴露的内容设置为exports或exports的属性,其他模块即可通过require来获取这些内容
CommonsJS规范
-
引入模块
- 使用require(“模块的路径”)函数来引入模块
-
引入自定义模块时
- 模块名要使用 ./ 或 ../开头
-
扩展名可以省略不写
(“./m1.js”)
中的
.js
就是扩展名 - 在CommonJS中,如果省略的js文件的扩展名,则node会自动为文件补全拓展名,
-
访问文件,优先访问
.js
结尾的文件 ,其次是
.json
结尾的文件,
最后是
.node
结尾的文件
// 引入文件名为m1的模块
const file = require("./m1.js")
console.log(file)
-
在定义模块时,模块中的内容默认是不能被外部看到的
- 可以通过exports来设置要向外暴露的内容
-
访问exports的方式有两种:
- exports
- module.exports
- 当我们在其他模块中引入当前模块时,require函数的返回的就是exports
- 可以将希望暴露给外部模块的内容设置为exports的属性
通过exports一个一个的导出值
m1.js文件
// 通过exports向外界暴露a b c 三个变量
exports.a = "孙悟空"
exports.b = "猪八戒"
exports.c = "沙和尚"
01_模块化.js文件
// 引入文件名为m1的模块
const file = require("./m1.js")
// 打印输出
console.log(file)
打印结果
{a: '孙悟空', b: '猪八戒', c: '沙和尚'}
通过module.exports同时导出多个值
m1.js文件
// 通过module.exports同时导出多个值
module.exports = {
a: "哈哈",
b: [1, 1],
c: () => {
console.log(111)
}
}
01_模块化.js文件
// 引入文件名为m1的模块
const file = require("./m1.js")
// 打印输出
console.log(file)
打印结果
{a: '哈哈', b: Array(2), c: ƒ}
-
引入核心模块时
- 直接写核心模块的名字即可
- 也可以在核心模块前添加node: ,加快查找速度
// 引入内置的核心模块"path"
const path = require("path")
// 引入内置的核心模块"path"
// 加 node: 是为了指明是核心模块
const path = require("node:path")
默认情况下,Node.js会将以下的内容视为CommonJS模块:
- 使用 .cjs为扩展名的文件
- 当前的package.json的type属性为commonjs时,扩展名为 .js 的文件
- 当前的 package.json不包含type属性时,扩展名为 .js 的文件
- 文件得到扩展名是mjs、cjs、node、js意外的值时(type不是module时)
require() 是同步加载模块的方法,所以无法用来加载ES6的模块,当我们需要在CommonJS中加载ES模块时
模块的包装
每一个CommonJS模块在执行时,外层都会被套上一个函数
(function(exports,require,module, _filename, _dirname) {
// 模块代码会被放到这里
});
模块都是包含在函数当中的
// 所写的模块
let a = 20
let b = 10
// 在执行时是套在模块里面的
(function(exports,require,module, _filename, _dirname) {
// 模块代码会被放到这里
let a = 20
let b = 10
});
证明模块确实运行到了函数里面,使用函数专有的arguments,
所有的实参都会封装到 arguments 中
// 所写的模块
let a = 20
let b = 10
// 打印arguments 观测模块是否运行到了函数里面
console.log(arguments)
控制台打印
Arguments(5) [{…}, ƒ, Module, 'D:\前端\NodeJs\02_模块化\m1.js', 'D:\前端\NodeJs\02_模块化', callee: ƒ, Symbol(Symbol.iterator): ƒ]
所以我们之所以能在CommonJS中模块中使用
exports
、
require
并不是因为他们是全局变量,他们实际上以为参数的形式传递模块
exports: 用来设置模块向外暴露的内容
require: 用来引入模块的方法
module: 当前模块的引用
__filename: 模块的路径
__dirname: 模块所在目录的路径