CJS
模块化
随着我们写的程序越来越复杂,如果我们把项目都编写在一个文件里,代码就会变得非常难以维护。模块化就是解决问题的关键
什么是模块化
模块简单来说就是一个个代码片段,它把写在一起的js代码,按照功能的不同把它拆分成一个个小的代码片段,这个代码片段
就是一个模块。早期的网页中是没有一个实质的模块化规范的,我们实现模块化的方式,就是最原始的通过srcipt标签来引入
多个js文件,这样就导致了无法选择引入模块哪些内容、在复杂的模块场景下非常容易出错(比如:引入的库,有相互依赖关系)
于是,我们急需在js中引入模块化规范
CommonJS 是第三方自定义的模块化系统,同时它也是node.js默认使用的模块化标准。在CommonJS中,一个js文件精神一个模块
使用
require
------ 使用require("模块的路径") 函数来引入模块,引入自定义模块时,模块名需要以 ./ 或者 ../ 开头,其中扩展名可以省略
比如 .js 就可以省略。在commonjs中如果省略js文件的扩展名,node会自动为文件补全扩展名,如果没有js,它会找json文件
如果既有js又有json,它会优先找js文件
---- 引入核心模块(node自带的模块)
直接写模块核心的名字即可,比如
const path = require("path")或者
const path = require("node:path") 加node是为了指明它是一个核心模块,能让它的查找速度更快一些,但是效果一样
怎么检查引入呢?如果引入了,模块的代码需要执行,如果执行了就说明引入成功了。此时,又有一个问题?
引入了什么
你可以使用一个变量去接收require,如果打印出来的是 {} 说明什么也没引进来。也就是说在定义模块时,我们模块中的内容默认
是不能被外部看到的,我们可以通过**exports **来设置向外部暴露的内容,其实common.js 本质就是一个闭包,所以它才能做到每个
模块作用域隔绝
exports
exports本身是一个对象,可以通过打印得知,而它有两种访问方式,exports 和 module.exports
当我们在其他模块中引入当前模块时 ,require 返回的就是export对象。简单来说,exports就是require的返回值,所以
我们可以将想要暴露的内容设置为exports的属性,比如:
exports.a = "你好"
此时我们再require它就不会是 {} 而是 {a: '你好'}
所以,我们可以随意的编写代码,当我们的内容想暴露给外部,就按上面做就行了,并且不止可以传值,也可以传变量、函数等等
暴露少用exports 没毛病,但是一旦我们暴露的多,就很麻烦,此时我们可以使用它的全等的第二种方式 module.exports
module.exports = {
a: "你好",
b: "[1,3,5]",
c: () => {
console.log(111)
}
}
但是你会发现 前面如果 exports === module.exports 为什么 不能直接 使用 exports 代替 module.exports 呢
module.exports,叫对象.属性,我们修改的是这个对象的属性(引用地址)
exports ,给变量赋值,只会影响变量自己
默认情况
默认情况下,node.js 会把以下内容设置为 CommonJS模块
- 使用.cjs 为扩展名的文件
- 当前的package.json 的type属性为 commonjs时,扩展名为.js 的文件
- 当前package.json 不包含type属性时,扩展名为 .js 的文件
- 文件的扩展名是 mjs 、cjs、json、node、js 以外的值时 (type不是module时)
文件夹作为模块
引入文件夹模块时,只需要引入主文件,一般叫 index.js 。比如有一个文件夹,里面有很多个字文件,字文件是为了服务index.js 而
存在的,它们并不需要被外界知道,它们只需要在index.js 里面引入就行,那我们直接在index.js 中暴露,然后
require引入文件夹名字即可
执行原理
所有的commonjs 的模块都会被包装在一个函数中
(function(exports, require, module, __filename, __dirname) {
})
比如我们有这么写的代码:
let a = 10 node在执行它时,会给代码外面套一层函数,也就是下面这样
(function(exports, require, module, __filename, __dirname) {
let a = 10
})
我们能用 exports, require, module是因为这些东西都是作为实参传进来的,它们不是全局变量,而是参数。
证明
证明它是运行在函数里,我们只需要找到一个只有函数里才有的东西,也就是 console.log(arguments),所有的实参都会
封装在arguments函数里。
__ filename, __ dirname 是什么
__ filename 是 当前模块的绝对路径
__ dirname 是 当前模块所在目录的路径