模块化
什么是node模块化
JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器。然而, JavaScript标准定义的 API 是为了构建基于浏览器的应用程序。并没有制定一个用于更广泛的应用程序的标准库。
CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准的缺陷。它的终极目标就是:提供一个类似 Python,Ruby 和 Java 语言的标准库。
CommonJS 就是模块化的标准,nodejs 就是 CommonJS(模块化)的实现。
CommonJS模块化会应用于nodejs、vue、react等等框架语言。
那我们在什么情况下使用模块化的标准进行开发呢
如果我们写一个稍微大型点的案例,这大型的案例代码肯定是比较多的,如果我们臃肿的写在一个js文件里面,那会使得我们我们文件体积非常大,代码量非常的多,那么就有极大的几率会写成的。
那么我们可以使用模块化,还给我们的项目进行模块化的区分(把不同的功能写在不同的文件夹当中),这样写的好处我们可以让模块复用性,减少代码的臃肿,为了我们在一次写项目的时候可以减少开发时间和提高效率,做准备的。
如何在node当中如何实现每一个js文件都是一个模块的呢
在node.js 当中每一个js的文件他就是一个模块,你虽然说都是每一个js文件都是一个模块,那他是怎么到底是怎么实现每个js文件是一个模块呢。
如何实现每个js文件都是一个模块的呢
在node.js 当中每一个js文件就是一个模块,其中每个模块他都被一个匿名的函数包裹住(每个js文件里面的所有代码都是被一个立即执行函数包裹住)
如何验证
如何验证node当中的js文件里面的代码都是被一个立即执行函数包裹组呢??
简单点来说,你当前写的node.js代码环境都是在函数作用域当中,而不是全局作用域当中
那有那些是在函数作用域当中有的,而不是全局作用域当中有的
那就是函数当中的arguments关键词,如果运行起来不报错那么就是被立即执行函数包裹住
那我们我们还可以用arguments.callee来找到当前arguments是指向那个函数
那我们还可以用arguments.callee + '' 的方式来把函数转换成字符串,这样就可以看到我们的立即执行函数了
console.log(arguments.callee + '')
/*
打印结果如下,他是一个函数体
function (exports, require, module, __filename, __dirname) {
console.log(arguments.callee + '')
}
*/
通过以上的方法可以验证出来,node.js当中的模块是被一个大的立即执行函数包裹住了,并且他有对应的5个参数(接下来就说这五个参数的意思)
模块当中,立即执行函数里面的五个参数
立即执行函数的五个形参(都是立即执行函数传递的形参,而不是全局的方法)
-
exports:是一个对象 专门用来暴露模块的数据 (本质上是通过module.exports这个对象暴露数据的)
-
require:require他是函数类型 专门用来引入模块的
-
module:module他是模块的意思,说白了就是你当前模块(js文件)里面的内容 给你返回出来,他是一个模块对象
-
__filename:__filename他是你当前文件的名字,可以以理解为他是文件的绝对路径(文件)
-
__dirname:__dirname他是当前文件夹的绝对路径(文件夹)
模块的分类
在node中,模块可以分为两类
一类是 Node 提供的模块,称为 核心 模块;另一类是用户编写的模块,称为 文件模块。
核心模块
核心模块部分在 Node 源代码的编译过程中,编译进了二进制执行文件。在 Node 进程启动时,部分核心模块就被直接加载进内存中,所以这部分核心模块引入时,文件定位和编译执行这两个步骤可以省略掉,并且在路径分析中优先判断,所以它的加载速度是最快的。
如:HTTP 模块 、URL 模块、Fs 模块都是 nodejs 内置的核心模块。 可以直接引入使用。
文件模块
文件模块则是在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程、速度相比核心模块稍微慢一些,但是用的非常多。 这些模块需要我们自己定义。 接下来我下 们看一下 nodejs 中的自定义模块。
CommonJS (Nodejs ) 中 自定义 模块的规定:
- 我们可以把公共的功能 抽离成为一个单独的 js 文件 作为一个模块,默认情况下面这个模块里面的方法或者属性,外面是没法访问的。如果要让外部可以访问模块里面的方法者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。
- 在需要使用这些模块的文件中,通过 require 的方式引入这个模块。这个时候就可以使用模块里面暴露的属性和方法。
实现一个简单的模块化
首先我们在index.js当中写两个功能性的函数,一个是求两个数的和,另一个是求两个数的差。代码如下
// 求出两个数字的和
function add (a, b) {
return a + b
}
// 求出两个数字的差
function minus (a, b) {
return a - b
}
const addResult = add(8, 5)
const minusResult = minus(8, 5)
// 并且我们打一下两个函数的执行结果
console.log(addResult, minusResult)
当前在index.js文件当中是有两个功能的,一个是求两个数的和,另一个是求两个数的差,如果math.js文件里面是有许多个功能逻辑的情况下
这时我们需要用模块化的角度来看的话,你可以认为index.js是一个大的项目,而这个项目当中有许多个小功能或者是模块,那么我们下一次写别的项目的时候,有许多种可能是用到当前项目里面的功能模块的。
那我就可以用模块化的实现把当前模块下面的功能给差散出来成单个模块,在进行一步的复用
模块化index.js文件里面的功能
首先我们要创建一个add.js和minus的文件用于模块化index.js文件里面的功能,并且要使用exports.名字/module.exports = {键值对} 这两种暴露对象的模块的方法都可以
然后我们在index.js 文件里面通过require(模块路径)的方式来引入我们刚才自定义的模块功能,注意我们通过exports.名字的方式暴露的模块,我们在用require引入的时候他是一个对象,所以说我们要用解构赋值的方式来解构出来我们当前的方法
minus.js 模块
// 暴露求出两个数字的差的功能 (我们暴露的是一个对象)
exports.minus = function minus(a, b) {
return a - b
}
add.js 模块
// 暴露求出两个数字的和的功能(我们暴露的是一个对象)
exports.add = function add(a, b) {
return a + b
}
index.js 模块
// 通过require引入对应的模块
// 这个模块是一个对象,我们用解构赋值的方式获得我们需要使用的方法
const {add} = require('./add')
const {minus} = require('./minus')
const addResult = add(8, 5)
const minusResult = minus(8, 5)
// 并且我们打一下两个函数的执行结果
console.log(addResult, minusResult)
总结
现在我们如何让两个模块之间互相关联起来,也就是互相通信
-
我们在其他模块当中使用exports暴露出去一个方法,当前模块要暴露出去方法给别的模块要使用exports
-
然后我们使用require函数进行引入模块,这时我们通过exports.名字 暴露出来的是一个对象,所以说我们在使用require函数引入的时候也是一个对象
module与exports之间的关系
其实在模块,module和exports都是可以暴露模块的,那他们之间的关系或者说是区别在那呢??
我们先分别打印出来module和exports
console.log(module, 'module')
console.log(exports, 'exports')
在上图可以看出来module当中也有exports,那么我们可以使用===符号来进行判断一下
console.log(module.exports === exports) // true
结果是true,说明exports和module.exports是一个东西
那他们两个的区别到底在那呢??
我们可以用exports和module.exports分别暴露出来一个模块试试看
exports方法暴露模块
exports.minus = function add (a, b) {
return a + b
}
这个是在原来的基础上赋值了一个新的属性值是一个函数在原先对象上面添加了一个属性(引用地址没有更改)
那我们用exports方法暴露模块的时候,赋值一个对象进行暴露模块进行暴露模块,会怎么样呢
export = {
music: function add (a, b) {
return a + b
}
}
这个export被赋值成了一个新的对象 会报错,因为你是赋值了一个对象{},这个export被赋值成了一个新的对象 则exports就不能暴力模块当中的数据了,因为他的引用地址被改变,就不能暴露对象了
那我们想通过从新赋值一个对象的形式暴露一个对象,我们可以通过module.exports的方法进行暴露模块
在上面你把一个新的对象像给了,exports = {} 进行暴露 那么就会报错,而你现在是把一个对象给了module.exports = {} 进行暴露 那么依然可以进行暴露
module.exports = {
minus: function add (a, b) {
return a + b
}
}
这个是在原来的module里面的exports基础上新的对象,而module里面的引用地址并没有被更改,这个module.export被赋值成了一个新的对象 则依然可以暴露模块的数据。
总结:module与exports之间的关系
module他是一个模块对象,其中这个模块对象里面有一个exports, 那么模块对象当中的exports和我们立即执行函数下面的exports有什么关系呢
这两个exports初始都是一个空对象{},那么这两个对象有什么关系呢 ??
那么我们可以用恒等来判断一下:module.exports === exports 结果是true
通过===来验证就可以说明这两个exports是同一个东西
那我们使用exports暴露模块,本质上也可以使用module.exports的方式暴力对象
path模块
在node当中我们引入node给我内置的模块,那么我们直接用require(模块的名字)引入即可,这样node会让为你这个是node当中核心模块
如果我们引入自定义模块的时候 要require(模块的路径)方式引入
什么是path模块
path模块是用来专门处理绝对路径的 如__filename(当前模块文件夹的绝对路径)
引入path模块
我们可以通过require的方式来引入path模块
const path = require('path')
接下来我会向大家简绍path模块当中常用的一些方法
path.parse(绝对路径)格式化(序列化)路径
const path = require('path')
console.log(path.parse(__filename))
path.parse(__filename)格式化(序列化)路径 一旦把路径传进去那么返回的是一个对象 不经常用
path.extname(绝对路径) 获取当前路径的后缀名
const path = require('path')
console.log(path.extname(__filename))
path.extname(__filename) 获取当前路径(查看文件)的后缀名 如果是文件夹的路径那么path.extname的值为空 常用
path.basename(绝对路径) 获取文件名字
const path = require('path')
console.log(path.basename(__filename))
path.basename(__filename) 获取当前路径后缀名前面的基础的名字(最后一个下反斜杠后面的名字)
path.dirname(__filename) 获取文件前面的路径
const path = require('path')
console.log(path.dirname(__filename))
path.dirname(__filename) 获取到最后一个反斜杠之前的路径
path.join(绝对路径1, 字符串,字符串....) 拼接路径
const path = require('path')
console.log(path.join(__filename, '巧克力很苦', '苦的'))
path.join(__filename, 'qkl', 'l') 拼接路径后面可以拼接多个路径
如:C:\Users\de'l'l\Desktop\CommonJS\index.js\巧克力很苦\苦的
自定义工程目录
当运行该模块的时候会,我们会自动生成目录或者是文件(也就是自动创建我们的工程目录)
index.js
const {file} = require('./file.js')
// 初始工程目录的数据
const programeData = {
name: '巧克力很苦', // 创建文件夹的名字
fileData: [ // 创建文件夹里面的数据 他是一个数组
{
name: 'css', // 创建一个名字是css的文件夹
type: 'dir' // type: dir 创建一个文件夹
},
{
name: 'js',
type: 'dir'
},
{
name: 'images',
type: 'dir'
},
{
name: 'index.html',
type: 'file' // type: file 创建一个index.html的文件
}
]
}
file(programeData)
file.js
const fs = require('fs')
const path = require('path')
exports.file = function (data) {
// 首先我们要在一下判断
// 如果巧克力很苦文件夹已经存在就不创建了,反之创建
const {name, fileData} = data
if (!fs.existsSync(name)) { // 如果文件夹不存在 那么就创建文件夹
fs.mkdirSync(name) // 创建文件夹
// 根据fileData数据创建指定的文件夹
fileData.forEach(item => {
// 根据item值也就是数组当中每一个对象的type值来判断,在进行创建不同的文件夹
const {type} = item
// 进行路径的拼接
const fullPath = path.join(__dirname, name, item.name)
if (type === 'dir') { // 如果是type值是dir那么就创建文件夹
fs.mkdirSync(fullPath) // 创建文件夹
} else {
fs.writeFileSync(fullPath, '我是index.html里面的内容') // 创建文件
}
})
}
}