Node.js是一个基于Chrome V8引擎的JavaScript运行环境。
1、浏览器是JavaScript的前端运行环境
2、Node.js是JavaScript的后端运行环境
3、Node.js中无法调用DOM和BOM等浏览器内置的API
1、LTS作为长期稳定版本,对于追求稳定性的企业级项目来说,应使用LTS版本的Node.js。
2、Current为新特性版本。
使用node -v
查看已安装Node.js的版本号
在终端中,使用esc键,能够快速清空当前已输入的命令,输入cls命令,可以清空终端。
fs模块是Node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件的操作需求。
fs.readFile()
方法,用来读取指定文件中的内容。
fs.writeFile()
方法,用来向指定的文件中写入内容。
如果在JavaScript代码中,使用fs模块来操作文件,则需要使用如下的方式先导入它:const fs = require('fs')
1、fs.readFile()的语法格式
使用fs.readFile()方法,可以读取指定文件中的内容,语法fs.readFile(path[, options],callback)
使用中括号包裹的是可选参数。
参数1:必选参数,字符串,表示文件的路径
参数2:可选参数,表示以什么格式的编码格式来读取文件
参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果
2、fs.readFile()示例代码
以utf8的编码格式,读取指定文件的内容,并打印err和dataStr的值:
//1、导入fs模块
const fs = require('fs')
//2、调用fs.reedFile()方法读取文件
// 参数1:读取文件的存放路径
// 参数2:读取文件时的编码格式,一般指定utf8
// 参数3:回调函数,拿到读取失败和成功的结果, err dataStr
fs.readFile('./files/1.txt','utf8',function (err,dataStr) {
//打印失败的结果,如果读取成功,则err的值为null
console.log(err)
console.log('------')
//打印成功的结果
console.log(dataStr)
})
如果读取错误,err的值为错误对象,dataStr则为undefined
3、判断文件是否读取成功
可以判断err对象是否为null,从而知晓文件读取的效果。通过err能够转为true,如果err为true则err不为null,则文件读取失败,打印失败的结果
//1、导入fs模块
const fs = require('fs')
//2、调用fs.reedFile()方法读取文件
// 参数1:读取文件的存放路径
// 参数2:读取文件时的编码格式,一般指定utf8
// 参数3:回调函数,拿到读取失败和成功的结果, err dataStr
fs.readFile('./files/11.txt','utf8',function (err,dataStr) {
if (err){
return console.log('读取文件失败' + err.message)
}
console.log('文件读取成功!' + dataStr)
})
1、fs.writeFile()的语法格式
使用fs.writeFile()
方法,可以向指定的文件中写入内容,语法fs.writeFile(file,data,[, options],callback)
使用中括号包裹的是可选参数。
参数1:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径
参数2:必选参数,表示要写入的内容
参数3:可选参数,表示以什么格式的编码格式来写入文件,默认是utf8
参数4:必选参数,文件写入完成后的回调函数
//1、导入fs模块
const fs = require('fs')
//2、调用fs.writeFile()方法,写入文件的内容
// 参数1:表示文件的存放路径
// 参数2:表示要写入的内容
// 参数3:回调函数
fs.writeFile('./files/2.txt','abcd',function(err) {
//如果文件写入成功,打印的err为一个null,但是当写入路径错误,打印的就不是null,而是一个真正的错误对象
console.log(err)
})
2、判断文件是否写入成功
可以判断err对象是否为null,从而知晓文件写入的效果。通过err能够转为true,如果err为true则err不为null,则文件读取失败,打印失败的结果
//1、导入fs模块
const fs = require('fs')
//2、调用fs.reedFile()方法读取文件
// 参数1:读取文件的存放路径
// 参数2:读取文件时的编码格式,一般指定utf8
// 参数3:回调函数,拿到读取失败和成功的结果, err dataStr
fs.writeFile('./files/2.txt','hfhfhfh',function (err) {
if (err){
return console.log('读取写入失败' + err.message)
}
console.log('文件写入成功!' )
})
使用fs文件系统模块,将素材目录下成绩.txt文件中的考试数据,整理到成绩-ok.txt文件中。
核心实现步骤
1、导入需要的fs文件系统模块
2、使用fsreadFile()
方法,读取素材目录下的成绩.txt文件
3、判断文件下是否读取失败
4、文件读取成功后,处理成绩数据
5、将处理完的成绩数据,调用fs.writeFile()
方法,写入到新文件成绩ok.txt中。
//1、导入fs模块
const fs = require('fs')
//2、调用fs.reedFile()方法读取文件
fs.readFile('./files/成绩-ok.txt','utf8',function (err,dataStr) {
//3、判断是否读取成功
if (err){
return console.log('读取写入失败' + err.message)
}
/* console.log('文件写入成功!' + dataStr)*/
//4.1先把成绩的数据,按照空格进行分割
const arrOld = dataStr.split(' ')
console.log(arrOld)
//4、2循环分割后的数据,对每一项数据,进行字符串的替换操作
const arrNew = []
arrOld.forEach(item => {
arrNew.push(item.replace('=',':'))
})
console.log(arrNew)
//4、3把数组中的每一项,进行合并,得到一个新的字符串
const newStr = arrNew.join('\r\n')
console.log(newStr)
//5、调用fs.writeFile()方法,把处理完毕的成绩,写入到新文件中
fs.writeFile('./files/成绩-ok.txt',newStr,function (err) {
if (err){
return console.log('写入文件失败!' + err.message)
}
console.log('成绩写入成功!')
})
})
在使用fs模块操作文件时,如果提供的操作路径是以/或…/开头的相对路径
时,很容易出现路径动态拼接错误的问题。
原因:代码在运行的时候,会以执行node 命令时所处的目录
,动态拼接出被操作文件的完整路径。
解决方案:在使用fs模块操作文件时,直接提供完整的路径,不要提供.或…/开头的相对路径,从而防止路径动态拼接的问题。
这里__dirname
表示当前文件所处目录,这里可以使用字符串的拼接进行路径的指定。
path模块是Node.js官方提供的,用来处理路径的模块,它提供了一系列的的方法和属性,用来满足用户对路径的处理需求。
path.join()
方法,用来将多个路径拼接成一个完整的路径字符串
path.basename()
方法,用来从路径字符串中,将文件名字解析出来
如果要在JavaScript代码中,使用path模块来处理路径,则需要使用如下的方式先导入它:const path = require('path')
1、path.join()语法格式
使用path.join()方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下:path.join([...paths])
参数解读:...paths
路径片段的序列
返回值:
2、path.join()代码示例
const path = require('path')
const fs = require('fs')
//注意: ../会抵消前面一层路径,多个则抵消多层路径
const pathStr = path.join('/a','/b/c','../','./d','e')
console.log(pathStr)//\a\b\d\e
//这里写代码的文件为06.path.join这个文件处于day01文件夹下,所以这里的__dirname就是day01
fs.readFile(path.join(__dirname,'/files/1.txt'),'utf8',function (err,dataStr) {
if (err){
return console.log(err.message)
}
console.log(dataStr)
})
path.basename()方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下path.basenem(path[, ext])
参数解读:path
必选参数,表示一个路径的字符串
ext
可选参数,表示文件扩展名
返回
表示路径中的最后一部分
const path = require('path')
const fpath = '/a/b/c/index.html'
const fullName = path.basename(fpath)
console.log(fullName)//index.html
const newfull = path.basename(fpath,'.html')
console.log(newfull)//index
使用 path.extname()方法,可以获取路径中的扩展名部分,语法格式如下:path.extname(path)
参数解读:
path
必选参数,表示一个路径字符串
返回:
返回得到的扩展名字符串
const path = require('path')
const fpath = '/a/b/c/index.html'
const fext = path.extname(fpath)
console.log(fext)//.html
案例要实现功能
将素材目录下的index.html页面拆成三个文件,分别是index.js index.css index.html并且将拆分出来的3个文件存放到clock目录中。
案例的实现步骤
1、创建两个正则表达式,分别用来匹配
//1.1导入fs文件系统模块
const fs = require('fs')
//1.2导入path路径处理模块
const path = require('path')
//1.3匹配<style></style>标签的正则
// 其中\s表示空白字符;\S表示非空白字符;* 表示匹配任意次
const regStyle = /<style>[\s\S]*<\/style>/
//1.4匹配<script></script>标签正则
const regScript = /<script>[\s\S]*<\/script>/
//2.1读取需要被处理的HTML文件
fs.readFile(path.join(__dirname,'/index.html'),'utf8',(err,dataStr) =>{
//2.2读取HTML文件失败
if (err) return console.log('读取HTML文件失败!' + err.message)
//2.3读取HTML文件成功后,调用对应的方法,拆解出css,js和HTML文件
resolveCSS(dataStr)
resolveJS(dataStr)
resolveHTML(dataStr)
})
//3.1处理css样式
function resolveCSS(htmlStr){
//3.2使用正则提取页面中的style标签
const r1 = regStyle.exec(htmlStr)
//3.3将提取出来的样式字典,做进一步处理
const newCSS = r1[0].replace('','')
//3.4将提取出来的css样式,写入到index.css文件中
fs.writeFile(path.join(__dirname,'./clock/index.css'),newCSS,err => {
if (err)return console.log('写入CSS样式失败!' + err.message)
console.log('写入CSS样式成功')
})
}
//4.1处理JS脚本
function resolveJS(htmlStr){
//4.2使用正则提取页面中的<script>标签
const r2 = regScript.exec(htmlStr)
//4.3将提取出来的脚本字符串,做进一步处理
const newJS = r2[0].replace('','')
//4.4将提取出来的js脚本,写入到index.js文件中
fs.writeFile(path.join(__dirname,'./clock/index.js'),newJS,err => {
if (err)return console.log('写入JavaScript脚本失败!' + err.message)
console.log('写入JS脚本成功!')
})
}
//5.1处理html文件
function resolveHTML(htmlStr){
//5.1使用字符串的replace方法,把内嵌的<style><script>标签,替换为外联的<link><script>标签
const newHTML = htmlStr
.replace(regStyle,'')
.replace(regScript,'')
//5.2将替换完成的html代码,写入到index.html文件中
fs.writeFile(path.join(__dirname,'./clock/index.html'),newHTML,err => {
if (err) return console.log('写入HTML文件失败!' + err.message)
console.log('写入HTML页面成功!')
})
}
fs.writeFile()方法只能用来创建文件,不能用来创建路径。重复调用fs.writeFile()写入同一个文件,新写入的内容会覆盖之前的旧内容
在网络节点中,负责消费资源的电脑,叫客户端,负责对外提供网络资源的电脑,叫做服务器
http模块是Node.js官方提供的,用来创建web服务器的模块,通过http模块提供的http.creatServer()
方法,就能把一台普通的电脑,变成一台Web服务器,从而对外提供Web资源服务,导入模块const http = require('http')
服务器和普通电脑的区别在于,服务器上安装了web服务器软件,例如:IIS、Apache等。通过安装这些服务器软件,就能把一台普通电脑变成一台web服务器。
在Node.js中,我们不需要使用IIS,Apache等这些第三方web服务器软件,因为我们可以基于Node.js提供的http模块,通过几行简单代码,就能充当服务器。
1、IP地址
IP地址就是互联网上每台计算机的唯一地址,因此IP地址具有唯一性。如果把“个人电脑”比作“一台电话”,那么“IP地址”就相当于“电话号码”,只有在知道对方IP地址的前提下,才能与对应的电脑之间进行数据通信。
IP地址的格式:通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是O~255之间的十进制整数。例如:用点分十进表示的IP地址(192.168.1.1)
2、域名和域名服务器
尽管IP地址能够唯一地标记网络上的计算机,但IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址。
IP地址和域名是一一对应的关系,这份对应关系存放在一种叫做域名服务器(DNS,Domain name server)的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供IР地址和域名之间的转换服务的服务器。
单纯使用IP地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。
在开发测试期间,127.0.0.1对应的域名是localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。
3、端口号
计算机中的端口号,就好像是现实生活中的门牌号一样。通过门牌号,外卖小哥可以在整栋大楼众多的房间中,准确把外卖送到你的手中。
同样的道理,在一台电脑中,可以运行成百上千个web服务。每个web服务都对应一个唯一的端口号。客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的web 服务进行处理。
1、导入HTTP模块const http = require('http')
2、创建web服务器实例const server = http.createServer()
3、为服务器实例绑定request事件,监听客户端发送来的网络请求
4、启动服务器
//1、导入http模块
const http = require('http')
//2、创建web服务器实例
const server = http.createServer()
//3、为服务器实例绑定request事件,即可监听客户端发送过来的网络请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req,res)=> {
//只要有客户端来请求我们的服务器,就会触发request事件,从而调用这个事件处理函数
console.log('Someone visit our web server')
})
//4、调用服务器实例.listen()方法,即可启动当前web服务器
server.listen(80,() =>{
console.log('http server running at http://127.0.0.1')
})
req请求对象
只要服务器接收到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数。如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下方式
//1、导入http模块
const http = require('http')
//2、创建web服务器实例
const server = http.createServer()
//3、为服务器实例绑定request事件,即可监听客户端发送过来的网络请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req) => {
//这里使用的ES6中的模板字符串为反引号,就是键盘左上角的‘~’按钮
const str = `Your request url is ${req.url},and request method is ${req.method}`
console.log(str)//Your request url is /,and request method is GET
})
server.listen(80,() =>{
console.log('server running at http://127.0.0.1')
})
res响应对象
在服务器的request事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下方式
//1、导入http模块
const http = require('http')
//2、创建web服务器实例
const server = http.createServer()
//3、为服务器实例绑定request事件,即可监听客户端发送过来的网络请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req,res) => {
//res是响应对象,它包含了与服务器相关的数据和属性,例如:
//要发送到客户端的字符串
const str = `Your request url is ${req.url},and request method is ${req.method}`
//res.end()方法的作用,向客户端发送指定内容,并结束这次请求的处理过程
res.end(str)
})
server.listen(80,() =>{
console.log('server running at http://127.0.0.1')
})
解决中文乱码问题
当调用res.end()方法,向客户端发送中文内容的时候,会出现乱码问题
//1、导入http模块
const http = require('http')
//2、创建web服务器实例
const server = http.createServer()
//3、为服务器实例绑定request事件,即可监听客户端发送过来的网络请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req,res) => {
const str = `您请求的URL地址是${req.url},请求的method类型是${req.method}`
//为了防止中文显示乱码的我呢提,需要设置响应头Content-Type的值为text/html;charest=utf-8
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(str)
})
server.listen(80,() =>{
console.log('server running at http://127.0.0.1')
})
核心实现步骤
1、获取请求的url地址
2、设置默认的响应内容为404 Not Found
3、判断用户请求的是否为/或/index.html首页
4、判断用户请求的是否为/oabout.html关于页面
5、设置Content-Type响应头,防止中文乱码
6、使用res.end()把内容响应给客户端
动态响应内容
//1、导入http模块
const http = require('http')
//2、创建web服务器实例
const server = http.createServer()
//3、为服务器实例绑定request事件,即可监听客户端发送过来的网络请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req,res) => {
const url = req.url
let content = '404 Not found!
'
if (url === '/' || url ==='/index.html'){
content = '首页
'
}else if (url === '/about.html'){
content = '关于页面
'
}
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(content)
})
server.listen(80,() =>{
console.log('server running at http://127.0.0.1')
})
模块化是指解决一个复杂问题时,自顶向下逐级把系统划分成若干模块的过程,对于整个系统来说,模块是可以组合、分解和更换的单元。
把代码进行模块化拆分的好处:提高代码复用性和可维护性,实现按需加载。使用模块化规范应考虑使用什么样的语法格式来引用模块,在模块中使用什么样的语法格式向外暴露成员。
Node.js中根据模块来源的不同,将模块分为3大类,分别是:
内置模块(内置模块是由Node.js官方提供的,例如fs、path、http等)
自定义模块(用户创建的每个.js文件,都是自定义模块)
第三方模块(由第三方模块开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要下载)
使用强大的require()方法,可以加载需要的内置模块,用户自定义模块,第三方模块进行使用。使用require调用其他模块时,可以不用写文件后缀名,他自己会找。例如:
//1、导入http模块
const http = require('http')
//2、加载用户的自定义模块
const custom = require('./custom.js')
//3、加载第三方模块
const moment = require('moment')
使用require()方法加载其他模块时,会执行被加载模块中的代码
和函数作用域类似,在自定义模块中定义的变量,方法等成员,只能在当前模块中被访问,这种模块级别的访问限制,叫做模块作用域。
我们导入的custom是一个空对象,这样防止了全局变量污染的问题。
1、module对象
在每个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息
2、module.exports对象
在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象。
3、共享成员时的注意点
使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准。
exports对象
由于module.exports单词写起来复杂,为了简化向外共享成员的代码,Node提供了exports对象。默认情况下,exports和module.exports指向同一个对象,还是以module.exports指向的对象为准。
exports和module.exports使用误区
简单来说,只要不是指向对象形式,就是添加属性。所以在同一个模块中不建议同时使用exports和module.exports
Node.js遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间如何相互依赖。
CommonJS规定:
1、每个模块内部,module变量代表当前模块
2、module变量是一个对象,它的exports属性(即module.exports)是对外的接口
3、加载某个模块,其实是加载该模块的module.exports属性,require()方法用于加载模块。
Node.js中的第三方模块又叫包。不同于Node.js中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。
由于Node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时,效率很低。包是基于内置模块封装出来的,提供了更高级、更方便的API,极大的提高了开发效率。
包和内置模块之间的关系,类似于jQuery和浏览器内置API之间的关系。
从https://www.npmjs.com/网站上搜索自己所需要的包从https://registry.npmjs.org/服务器上下载自己需要的包
这个包管理工具的名字叫做Node Package Manager(简称npm包管理工具),这个包管理工具随着Node,js的安装包一起被安装到了用户的电脑上。
使用时在官网搜索moment,进入后Documentation上右键在新标签页中打开则可显示具体使用方法。
在项目中安装包的命令
npm install 包的完整名称,也可简化为 npm i 包的完整名称
格式化时间的高级做法
1、使用npm包管理工具,在项目中按安装格式化时间的包moment
2、使用require()导入格式化时间的包
3、参考moment的官方API文档对事件进行格式化
//1、导入需要的包
//注意导入的名称,就是装包的名称
const moment = require('moment')
//
const dt = moment().format('YYYY-MM-DD HH:mm:ss')
console.log(dt)//2022-08-22 17:07:41
初次装包后多了哪些文件
初次装包完成后,在项目文件夹下多一个叫做node_modules 的文件夹和package-lock.json的配置文件。
node modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包。package-lock.json配置文件用来记录node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
包的语义化版本规范
包的版本号是以“点分十进制”形式进行定义的,总共有三位数字,例如2.24.0其中每一位数字所代表的的含义如下:
第1位数字:大版本
第2位数字:功能版本
第3位数字:Bug修复版本
版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零。
npm规定,在项目根目录中,必须提供一个叫做package.json
的包管理配置文件。用来记录与项目有关的一些配置信息。例如:
项目的名称、版本号、描述等
项目中都用到了哪些包
哪些包只在开发期间会用到
那些包在开发和部署时都需要用到
在团队共享开发中,需要同时剔除node_modules
,所以在协同开发时,需要将其添加到.gitignore忽略文件中。
快速创建package.json
npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建package.json这个包管理。
//作用:在执行命令所处的目录中,快速创建package.json文件
npm init -y
上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格。运行 npm install命令安装包的时候,npm包管理工具会自动把包的名称和版本号,记录到package.json中。
Dependencies节点为package.json中记录使用npm install命令安装了那些包
一次性安装所有的包
当我们拿到一个剔除了node_modules的项目之后,需要先把所有的包下载到项目中,才能将项目运行起来。不然会报错,入没有moment包Error: Cannot find module ' moment'
所以,我们需要一次性安装所有的包,也同Java更新依赖一样。执行npm install命令时,npm包管理工具会先读取 package.json 中的 dependencies 节点,读取到记录的所有依赖包名称和版本号之后,npm包管理工具会把这些包一次性下载到项目中
卸载包
可以运行npm uninstall命令,来卸载指定的包npm uninstall moment
devDependencies节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中。与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。
//安装指定的包,并记录到devDependencies节点中
npm i 包名 -D
//注意:上述命令是简写形式,等价于下面完整写法
npm install 包名 --save-dev
为了更方便的切换下包镜像资源,安装nrm小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源,这里的taobao改名为了npmmirror
包的分类
使用npm包管理工具下载的包,共分为两大类,分别为项目包和全局包
1、项目包:那些被安装到项目的node_modules目录中的包,都是项目包。
项目包又分为两类,分别是:
开发依赖包(被记录到devDependencies节点中的包,只在开发期间会用到)
核心依赖包(被记录到dependencies节点中的包,在开发期间和项目上线之后都会用到)
2、全局包:在执行npm install命令时,如果提供了-g参数,则会把包安装为全局包。
全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_modules
目录下。
只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可。
3、i5ting_toc
i5ting_toc 是一个可以把 md 文档转为 html 页面的小工具,使用步骤如下:
规范的包结构
一个规范的包,它的组成结构,必须符合以下 3 点要求:
① 包必须以单独的目录而存在
② 包的顶级目录下要必须包含 package.json 这个包管理配置文件
③ package.json 中必须包含 name,version,main 这三个属性,分别代表包的名字、版本号、包的入口。
模块在第一次加载后会被缓存。 这也意味着多次调用 require() 不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。
内置模块是由 Node.js 官方提供的模块,内置模块的加载优先级最高。
例如,require(‘fs’) 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫做 fs。
使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/
这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
① 按照确切的文件名进行加载
② 补全 .js 扩展名进行加载
③ 补全 .json 扩展名进行加载
④ 补全 .node 扩展名进行加载
⑤ 加载失败,终端报错
如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ‘./’ 或 ‘…/’ 开头,则 Node.js 会从当前模块的父
目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在 ‘C:\Users\itheima\project\foo.js’ 文件里调用了 require(‘tools’),则 Node.js 会按以下顺序查找:
① C:\Users\itheima\project\node_modules\tools
② C:\Users\itheima\node_modules\tools
③ C:\Users\node_modules\tools
④ C:\node_modules\tools
当把目录作为模块标识符,传递给 require() 进行加载的时候,有三种加载方式:
① 在被加载的目录下查找一个叫做 package.json 的文件,并寻找 main 属性,作为 require() 加载的入口
② 如果目录里没有 package.json 文件,或者 main 入口不存在或无法解析,则 Node.js 将会试图加载目录下的 index.js 文件。
③ 如果以上两步都失败了,则 Node.js 会在终端打印错误消息,报告模块的缺失:Error: Cannot find module ‘xxx’