这是我参与 [第五届青训营] 笔记创作活动的第七天。
终端中的快捷键
在Windows的powershell或cmd终端中,我们可以通过如下快捷键,来提高终端的操作效率:
1使用⬆键,可以快速定位到上一次执行的命令
②使用tab键,能够快速补全路径
③使用esc键,能够快速清空当前已输入的命令
④、cls清空当前终端里面的内容
fs文件系统模块
2.1什么是fs文件系统模块
fs 模块是Nodejs官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求。例如:
fs.readFile()方法,用来读取指定文件中的内容
fs.writeFile()方法,用来向指定的文件中写入内容
如果要在JavaScript代码中,使用fs模块来操作文件,则需要使用如下的方式先导入它:
1 const fs = require( 'fs');
2.2读取指定文件中的内容
1、fs.readFile()的语法格式
使用fs.readFile()方法,可以读取指定文件中的内容,语法格式如下:
fs.readFile(path[, options], callback);
参数解读:
●参数1:必选参数,字符串,表示文件的路径。
●参数2:可选参数,表示以什么编码格式来读取文件。
●参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
2、fs.readFile()的示例代码:
以utf8的编码格式,读取指定文件的内容,并打印err 和dataStr 的值:
const fs = require('fs')
fs.readFile('./files/hello.txt','utf8',function(err,dataStr){
// 打印失败的结果 如果成功err的值为null
// 如果读取失败则err的值为错误对象, dataStr的值为undefined
console.log(err)
console.log( '-----')
// 打印成功的结果
console.log(dataStr)
})
判断文件是否读取成功
const fs = require('fs')
fs.readFile('./files/hello.txt','utf8',function(err,dataStr){
// 打印失败的结果 如果成功err的值为null
// 如果读取失败则err的值为错误对象, dataStr的值为undefined
if(err){
return console.log('文件读取失败!' + err.message);
}
console.log('文件读取成功!' + dataStr);
})
2.3向指定的文件中写入内容
1、fs.writeFlie()的语法格式
使用fs.writeFile()方法,可以向指定的文件中写入内容,语法格式如下:
fs.writeFile(file,data[,option],callback)
参数1∶必选参数,需要指定一个文件路径的字符串,表示文件的存放路径。
参数2:必选参数,表示要写入的内容。
参数3:可选参数,表示以什么格式写入文件内容,默认值是utf8。
参数4:必选参数,文件写入完成后的回调函数。
示例代码:
const fs = require('fs')
fs.writeFile('./files/hello.txt','abcdefg',function(err){
// 如果文件写入成功,err的值为null
// 如果文件写入失败,err的值为一个错误对象
console.log(err);
})
2.4路径动态拼接问题
在使用fs模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误的问题。
原因:代码在运行的时候,会以执行node命令时所处的目录,动态拼接出被操作文件的完整路径。
解决办法:
1、直接提供一个完整的文件存放路径,可以防止路径动态拼接的问题(问题:移植性非常差,不利于维护)
2、__dirname 表示当前文件所处的目录,(比较常用)
const fs = require('fs')
fs.readFile(__dirname + '/files/hello.txt','utf8',function(err,dataStr){
// 打印失败的结果 如果成功err的值为null
// 如果读取失败则err的值为错误对象, dataStr的值为undefined
console.log(err)
console.log( '-----')
// 打印成功的结果
console.log(dataStr)
path路径模块
3.1什么是path路径模块
path模块是Node.js 官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。
例如:
path.join()方法,用来将多个路径片段拼接成一个完整的路径字符串
path.basename()方法,用来从路径字符串中,将文件名解析出来
如果要在JavaScript 代码中,使用path模块来处理路径,则需要使用如下的方式先导入它:
1 const path = require( 'path')
3.2路径拼接
1、path.join()的语法格式
使用path.join()方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下
path.join([...paths])
参数解读:
1、...path路径片段的序列
2、返回值:string
示例代码:
const path = require('path')
const pathStr = path.join('/a','/b/c','../','./d','e');
console.log(pathStr);
const pathStr2 = path.join(__dirname,'./files/hello.txt')
console.log(pathStr2);
注意:今后凡是涉及到路径拼接的操作,都要使用path.join()方法进行处理。不要直接用+进行字符串的拼接
3.3获取路径中的文件名
1、path.basename()的语法格式
使用path.basename()方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下:
path.basename(path[, ext])
参数解读:
1、path 必选参数,表示一个路径的字符串
2、ext 可选参数,表示文件扩展名
3、返回: 表示路径中的最后一部分
示例代码:
const path = require('path')
const fpath = '/a/b/c/index.html'
var fullName = path.basename(fpath)//index.html
console.log(fullName);
var nameWithoutExt = path.basename(fpath,'.html')
console.log(nameWithoutExt);//index 删除了扩展名
3.4如何获取路径中的文件扩展名
path.extname()的语法格式
1 path.extname(path)
参数解读:
1、path 必选参数,表示一个路径的字符串
2、返回: 返回得到的扩展名字符串
代码示例:
const fpath = '/a/b/c/index.html'
// 获取文件扩展名
const fext = path.extname(fpath)
console.log(fext);
时钟案例
目的:
将index.html页面里的 css html js代码拆分成三个文件,分别是:
- index.css
- index.html
- index.js
- 并且将拆分出来的3个文件,存放到clock目录中
实现的步骤: 创建两个正则表达式,分别用来匹配和标签 使用fs模块,读取需要被处理的htmml文件 自定义resolveCSS方法,来写入 index.css 样式文件 自定义 resolveJS方法,来写入 index.js 脚本文件 自定义 resolveHTML方法,来写入 index.html文件
注意:
1、fs.writeFile()方法只能用来创建文件,不能用来创建路径
2、重复调用fs.writeFile()写入同一个文件,新写入的内容会覆盖之前的旧内容
http模块
http模块是Node.js官方提供的、用来创建web服务器的模块。通过 http模块提供的 http.createServer()方法,就能方便的把一台普通的电脑,变成一台Web 服务器,从而对外提供 Web资源服务。
如果要希望使用http模块创建Web服务器,则需要先导入它:
const http = require( 'http')
服务器相关概念
IlP地址就是互联网上每台计算机的唯一地址,因此IP地址具有唯一性。如果把“个人电脑”比作“一台电话”,那么“IP地址”就相当于“电话号码”,只有在知道对方IP地址的前提下,才能与对应的电脑之间进行数据通信。
IP地址的格式:通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。例如:用点分十进表示的IP地址(192.168.1.1)
注意:
1、互联网中每台Web服务器,都有自己的IP地址,例如:可以在windows的终端中运行 ping www.baidu.com命令,即可查看到百度服务器的IP地址
2、在开发期间,自己的电脑既是一台服务器,也是一个客户端,为了方便测试,可以在自己的浏览器中输入127.0.0.1这个IP地址,就能把自己的电脑当作一台服务器进行访问了
域名和域名服务器
尽管IР地址能够唯一地标记网络上的计算机,但IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址。
IP地址和域名是——对应的关系,这份对应关系存放在一种叫做域名服务器(DNS, Domain name server) 的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供IРP地址和域名之间的转换服务的服务器。
注意:
1、单纯使用IP地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。
2、在开发测试期间,127.0.0.1对应的域名是localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。
端口号
一台电脑中,可以运行很多的Web服务。每个Web服务都对应一个唯一的端口号。客户端发送过来的网络请求,通过端口号,可以被准确的交给对应的Web服务进行处理
注意:
1、每个端口号不能同时被多个Web服务占用
2、在实际应用中,URKL中的80端口可以被省略
创建最基本的Web服务器
1、创建web服务器的基本步骤
1、导入 http模块
在自己的电脑上创建一个Web服务器,从而对外提供Web服务,需要导入http模块:
const http = require('http')
2、创建web服务器实例
调用http.createServer()方法,即可快速创建一个web服务器实例:
const server = http.createServer()
3、为服务器实例绑定request事件,监听客户端的请求
//使用服务器实例的.on()方法,为服务器绑定一个request事件
server.on('request',(req,res)=>{
//只要有客户端来请求我们自己的服务器,就会触发request事件,从而调用这个事件处理函数
console.log('Someone visit our web server')
})
4、启动服务器
//调用server.listen(端口号,cb回调)方法,即可启动web服务器
server.listen(80,()=>{
console.log('http server running at http://127.0.0.1')
})
2、req请求对象
只要服务器接收到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方法:
server.on('request',(req) => {
//req是请求对象,它包含了与客户端相关的数据和属性,例如:
//req.url是客户端请求的URL地址
//req.method是客户端的method请求类型
const str = `Your request url is ${req.url},and request method is ${req.method}`
console.log(s)
})
3、res响应对象
在服务器的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)
})
4、解决中文乱码问题
当调用res.end()方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设置内容的编码格式
server.on('request',(req,res)=>{
//发送的内容包含中文
const str = `您请求的url地址是${req.url},请求的method类型是${req.method}`
//为了防止中文显示乱码问题,需要设置响应头Content-Type的值为text/html; charset=utf-8
res.setHeader('Content-Type','text/html; charset=utf-8')
//把包含中文的内容,响应给客户端
res.end(str)
})
根据不同的url响应不同的html内容
1、核心实现步骤
①、获取请求的url地址
②、设置默认的响应内容为404NotFound
③、判断用户请求的是否为/或/index.html首页
④、判断用户请求的是否为/about.html关于页面
⑤、设置Content-Type响应头,防止中文乱码
⑥、使用res.end()把内容响应给客户端
2、动态响应内容
const http = require('http')
const server = http.createServer();
server.on('request',(req,res)=>{
const url = req.url //1、获取请求的url地址
let content = '<h1>404 Not Found</h1>'//2、设置默认内容为404 Not Found
if(url === '/' || url === '/index.html'){
content = '<h1>首页</h1>'//3、用户请求的是首页
}else if(url === '/about.html'){
content = '<h1>关于页面</h1>'//4、用户请求的是关于页面
}
res.setHeader('Content-Type','text/html; charset=utf-8')//5、设置Content-Type响应头,防止中文乱码
res.end(content)//6、把内容发送给客户端
})
实现clock时钟的web服务器
1、核心思路
把文件的实际存放路径,作为每个资源的请求url地址
2、实现步骤
①、导入需要的模块
// 1.1导入http模块
const http = require('http')
//1.2导入fs文件系统模块
const fs = require('fs')
// 1.3导入path路径处理模块
const path = require('path')
②、创建基本的web服务器
// 2.1创建web服务器
const server = http.createServer()
// 2.2监听web服务器的request事件
server.on('request',(req,res)=>{
})
// 2.3启动web服务器
server.listen(80,()=>{
console.log('server listen at http://127.0.0.1');
})
③、将资源的请求url地址映射为文件的存放路径
// 3.1获取到客户端请求的url地址
const url = re1.url
// 3.2把请求的url地址,映射为本地文件的存放路径
const fpath = path.join(__dirname,url)
④、读取文件内容并响应给客户端
// 4.1根据映射过来的文件路径读取文件
fs.readFile(fpath,'utf8',(err,dataStr)=>{
// 4.2读取文件失败后,向客户端响应固定的“错误消息”
if(err){
return res.end('404 Not Found')
}
// 4.3读取文件成功后,将读取成功的内容响应给客户端
res.end(dataStr)
})
⑤、优化资源的请求路径
// 优化资源的请求路径
// 5.1预定义空白的文件存放路径
let fpath = ''
if(url === '/'){
// 5.2如果请求的路径为/,则手动指定文件的存放路径
fpath = path.join(__dirname,'./clock/index.html')
}else{
//5.3如果请求的路径不为/,则动态拼接文件的存放路径
fpath = path.join(__dirname,'./clock',url)
}
模块化
模块化的基本概念
1.1什么是模块化
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元
变成领域的模块化,就是遵守固定的规则,把一个大文件拆成独立并相互依赖的多个小模块
把代码进行模块化拆分的好处: 1、提高了代码的复用性
2、提高了代码的可维护性
3、可以实现按需加载
1.2模块化规范
模块化规范就是对代码进行模块化的拆分和组合时,需要遵守的那些规则
模块化规范的好处:大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用。
Node.js中的模块化
2.1Node.js中的模块分类
Node.js中根据模块来源的不同,将模块分成了3大类,分别是:
1、内置模块(内置模块是由Node.js官方提供的,例如fs、path、http等)
2、自定义模块(用户创建的每个.js文件,都是自定义模块)
3、第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
2.2加载模块
使用强大的require()方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。
//1、加载内置的fs模块
const fs = =require('fs')
//2、加载用户的自定义模块
const custom = require('./custom.js')
//3、加载第三方模块
const moment = require('moment')
注意:使用require()方法加载其他模块时,会执行被加载模块中的代码
2.3Node.js中的模块作用域
模块作用域:和函数作用域相似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
模块作用域的好处: 防止了全局变量污染的问题
2.4向外共享模块作用域中的成员
-
modle对象,在每个.js的自定义模块中都有,储存和当前模块有关的信息
-
module.exports对象,在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。外界用require()方法导入自定义模块时,得到的就是module.exports所指向的对象。
-
共享成员时的注意点,使用require()方法导入模块时,导入的结果,永远以moudule.exports指向的对象为准。
-
exports对象,由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node提供了exports对象。默认情况下,exports和module.exports指向同一个对象。最终的共享结果还是以module.exports指向的对象为准。
-
exports和module.exports的使用误区:
①、使用require()模块时,得到的永远是module.exports指向的对象
2.5Node.js中的模块化规范
Node.js遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间如何相互依赖。
CommonJS规定:1、每个模块内部,module变量代表当前模块
2、module变量是一个对象,它的exports属性(即module.exports)是对外的接口
3、加载某个模块,其实是加载该模块的module.exports属性。require()方法用于加载模块。
npm与包
3.1包
Node.js中的第三方模块又叫做包,是基于内置模块封装出来的,提供了更高级更方便的API,极大的提高了开发效率
不同于Node.js中的内置模块与自定义模块,包是由第三方个人或团队开发出来的。
搜索包的网站:www.npmjs.com/
下载包的服务器:registry.npmjs.org/
3.2npm初体验
1、格式化事件的传统做法:①、创建格式化时间的自定义模块
②、定义格式化时间的方法
③、创建补零函数
④、从自定义模块中导出格式化时间的函数
⑤、导入格式化时间的自定义模块
⑥、调用格式化时间的函数
2、格式化时间的高级做法:①、使用npm包管理工具,在项目中安装格式化时间的包moment
②、使用require()导入格式化时间的包
③、参考moment的官方API文档对时间进行格式化
3、在项目中安装包:
npm install 包的完整名称
//简写
npm i 包的完整名称
4、初次装包后的文件目录
初次装包完成后在项目文件夹下多出一个node_modules的文件夹和package-lock.json的配置文件
node_modules的文件夹:用来存放所有已安装到项目的包。require()导入第三方包时,就是从这个目录中查找并加载包
package-lock.json的配置文件:用来记录node_modules目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等
5、安装指定版本的包
用@指定
npm i moment@2.22.2
6、包的语义化版本规范
包的版本号事宜“点分十进制”形式定义,总共有3位数字
第一位数字:大版本
第二位数字:功能版本
第三位数字:Bug修复版本
版本号提升规则:只要前面的版本号增长了,则后面的版本号归零
3.3包管理配置文件
npm规定,在根目录中,必须提供一个叫做package.json的包管理配置文件。用来记录与项目有关的一些配置信息。如:
- 项目的名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 哪些包在开发和部署时都需要用到
1、多人协作的问题
问题:第三方包体积过大,不方便团队成员之间共享项目代码
解决方案:共享时剔除node_modules
2、如何记录项目中安装了哪些包
在项目根目录中,创建一个叫做package.json的配置文件,即可用来记录项目中安装了哪些包。从而方便剔除node_modules目录之后,在团队成员之间共享项目的源代码。
注意:在项目开发中,一定要把node_modules文件夹,添加到.gittignore忽略文件中。
3、快速创建package.json
npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建package.json这个包管理配置文件:
npm init -y
注意:1、上述命令只能在英文的目录下成功运行。
2、运行npm install 命令安装包的时候,npm包管理工具会自动把包的名称和版本号记录到package.json中
4、dependencies节点
package.json文件中,有一个dependencies节点,专门用来记录使用npm install命令安装了哪些包
5、一次性安装所有的包
当我们拿到一个剔除了node_modules的项目之后,需要先把所有的包下载到项目中,才能将项目运行起来。否则报错
Error: can not find module ''
可以运行npm install 命令(或npm i)一次性安装所有的依赖包
6、卸载包
可以运行npm uninstall 命令来卸载指定的包
注意:使用npm uninstall命令卸载包后,自动从package.json的dependencies中移除掉
7、devDependencies节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到devDependencies节点中
与之对应,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。
命令:
npm i 包名 -D
npm install 包名 --save-dev
3.4解决下包速度慢的问题
1、淘宝NPM镜像服务器
镜像是一种文件储存形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像
2、切换npm的下包镜像源
//查看当前的下包镜像源
npm config get registry
//将下包的镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
//检查镜像源是否下载成功
npm config set registry
3、nrm
为了更方便的切换下包的镜像源,我们可以安装nrm工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源。
//通过npm包管理器,将nrm安装为全局可用的工具
npm i nrm -g
//查看所有可用的镜像源
nrm ls
//将下包的镜像源切换为taobao镜像
nrm use taobao
3.5包的分类
1、项目包
被安装到项目的node_modules目录中的包都是项目包。
项目包分为:1、开发依赖包(被记录到devDependencies节点中的包,只在开发期间会用到)
2、核心依赖包(被记录到dependencies节点中的包,在开发期间和项目上线之后都会用到)
npm i 包名 -D //开发依赖包
npm i 包名 //核心依赖包
2、全局包
在执行npm install 命令时,如果提供了-g参数,则会把包安装为全局包
全局包被安装到:C:\Users\用户目录\AppData\Roaming\npm\node_modules目录下
npm i 包名 -g
npm uninstall 包名 -g
注意:
1、只有工具性质的包,才有 全局安装的必要性。因为它们提供了好用的终端命令
2、判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可
3、i5ting_toc
i5ting_toc是一个可以把md文档转为html页面的小工具,使用步骤如下:
# 将i5ting_toc安装为全局包
npm install -g i5ting_toc
# 调用isting_toc,轻松实现md转html功能
i5ting_toc -f 要转换的md文件路径 -o
3.6规范包的结构
一个规范的包,组成结构必须符合:1、包必须以单独的目录而存在
2、包的顶级目录下必须要包含package.json这个包管理配置文件
3、package.json中必须包含name,version,main这三个属性,分别代表包的名字、版本号、包的入口
注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考如下网站:yarnpkg.com/zh-Hans/doc…
3.7开发属于自己的包
1、需要实现的功能
①、格式化日期
②、转义HTML中的特殊字符
③、还原HTML中的特殊字符
2、初始化包的基本结构
①、新建sulli_tools文件夹,作为包的根目录
②、在文件夹中新建:1、package.json(包管理配置文件)
2、index.js(包的入口文件)
3、README.md(包的说明文档)
3、初始化package.json
{
"name":"sulli_tools",
"version":"1.0.0",
"main":"index.js",
"description": "提供了格式化时间,HTMLEscape的功能",
"keywords": ["sulli","dateFormat","escape"],
"license": "ISC"
}
4、在index.js中定义格式化时间的方法
// 格式化时间的方法
function dateFormat(dateStr){
const dt = new Date(dateStr)
const y = dt.getFullYear()
const m = padZero(dt.getMonth() + 1)
const d = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
// 补零的方法
function padZero(n){
return n > 9 ? n : '0' + n
}
// 向外暴露需要的成员
module.exports = {
dateFormat
}
5、在index.js中定义转义HTML的方法
// 转义HTML的方法
function htmlEscape(htmlStr){
return htmlStr.replace(/<|>|"|&/g,(match)=>{
switch(match){
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '$amp;'
}
})
}
6、在index.js中定义还原HTML的方法
// 还原HTML的方法
function htmlUnEscape(str){
return str.replace(/<|>|"|$amp;/g, (match)=>{
switch(match){
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '$amp;':
return '&'
}
})
}
7、将不同的功能进行模块化的拆分
①、将格式化时间的功能,拆分到src/dateFormat.js中
②、将处理HTML字符串的功能,拆分到src/htmlEscape.js中
③、在index.js中,导入两个模块,得到需要向外共享的方法
④、在index.js中,使用module.exports把对应的方法共享出去
8、编写包的说明文档
在README.md中,写包的说明信息
3.8发布包
1、注册npm账号
2、登录npm账号
npm账号注册完成后,可以在终端中执行npm login命令,依次输入用户名、密码、邮箱后,即可登录成功。
注意:在运行npm login命令之前,必须先把下包的服务器地址切换为npm的官方服务器。否则会导致发布包失败。
3、把包发布到npm上
将终端切换到包的根目录之后,运行npm publish命令,即可将包发布到npm上。(注意:包名不能雷同)
4、删除已发布的包
运行npm unpublish 包名 --force命令,即可从npm删除已发布的包
注意:1、npm unpublish命令只能删除72小时以内发布的包
2、npm unpublish删除的包,在24小时内不允许重复发布
3、发布包的时候要慎重,尽量不要往npm上发布没有意义的包
模块的加载机制
4.1优先从缓存中加载
模块在第一次加载后会被缓存。这也意味着多次调用require()不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。
4.2内置模块的加载机制
内置模块是由Node.js官方提供的模块,内置模块的加载优先级最高
4.3自定义模块的加载机制
使用require()加载自定义模块时,必须指定以./或../开头的路径标识符。在加载自定义模块时,如果没有指定./或../这样的路径标识符,则node会把它当作内置模块或第三方模块进行加载。
在使用require()导入自定义模块时,如果省略了文件的扩展名,则Node.js会按顺序分别加载以下的文件:
1、按照确切的文件名进行加载
2、补全.js扩展名进行加载
3、补全.json扩展名进行加载
4、补全.node扩展名进行加载
5、加载失败,终端报错
4.4第三方模块的加载机制
如果传递给require()的模块标识符不是一个内置模块,也没有以'./'或'../'开头,则Node.js会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录
4.5目录作为模块
当把目录作为模块标识符,传递给require()进行加载的时候,有三种加载方式:
1、在被加载的目录下查找一个叫做package.json的文件,并寻找main属性,作为require()加载的入口。
2、如果目录里没有package.json文件,或者main入口不存在或无法解析,则Node.js将会试图加载目录下的index.js文件
3、如果以上两步都失败了,则Node.js会在终端打印错误消息,报告模块的确实:Error:Cannot find module 'xxx'
Express
初识Express
1.1Express简介
Express是基于Node.js平台,快速、开发、极简的Web开发框架,作用与Node.js内置的http模块类似,是专门用来创建Web服务器的。
Express本质:就是一个npm上的第三方包,提供了快速创建Web服务器的便捷方式
Express能做什么:
对前端程序员最常见的两种服务器:1、Web网站服务器:专门提供Web网页资源的服务器
2、API接口服务器:专门对外提供API接口的服务器
使用Express,可以更方便、快速的创建Web网站的服务器或API接口的服务器
1.2Express的基本使用
1、安装
在项目所处的目录中运行终端命令
npm i express
2、创建基本的Web服务器
// 1、导入express
const express = require('express')
// 2、创建web服务器
const app = express()
// 3、调用app.listen(端口号,启动成功后的回调函数),启动服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
3、监听GET请求
通过app.get()方法,可以监听客户端的GET请求,具体语法格式如下:
//参数1:客户端请求的URL地址
//参数2:请求对应的处理函数
req:请求对象(包含了与请求相关的属性与方法)
res:响应对象(包含了与响应相关的属性和方法)
app.get('请求URL', (req,res)=>{
})
4、监听POST请求
通过app.post()方法,可以监听客户端的POST请求,具体语法格式如下:
//参数1:客户端请求的URL地址
//参数2:请求对应的处理函数
req:请求对象(包含了与请求相关的属性与方法)
res:响应对象(包含了与响应相关的属性和方法)
app.post('请求URL', (req,res)=>{
})
5、把内容响应给客户端
可以通过res.send()方法,可以把处理好的内容,发送给客户端
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({name:'zs',age:20,gender:'男'})
})
app.post('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({name:'zs',age:20,gender:'男'})
})
6、获取URL中携带的查询参数
通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数
app.get('/',(req,res)=>{
//req.query默认是一个空对象
//客户端使用?name=zs&age=20这种查询字符串形式,发送到服务器的参数
//可以通过req.query对象访问到,例如:
//req.query.name req.query.age
console.log(req.query)
})
7、获取URL中的动态参数
通过req.params对象,可以访问到URL中,通过 : 匹配到的动态参数:
//URL 地址中,可以通过 :参数名 的形式,匹配动态参数值
app.get('/user/:id',(req,res)=> {
//req.params默认是一个空对象
//里面存放着通过 : 动态匹配到的参数值
console.log(req.params)
})
1.3托管静态资源
1、express.static()
express提供了一个非常好用的函数,叫做express.static(),通过它,我们可以非常方便地创建一个静态资源服务器,例如,通过如下代码就可以将public目录下的图片、CSS文件、Javascript文件对外开放访问了:
app.use(express.static('public'))
注意:Express在指定的静态目录中查找文件,并对外提供资源的访问路径。因此,存放静态文件的目录名不会出现在URL中
2、托管多个静态资源目录
如果要托管多个静态资源项目,请多次调用express.static()函数:
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件。
3、挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
app.use('/public',express.static('/public'))
1.4nodemon
1、为什么要使用nodemon
在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后再重新启动,非常繁琐。
使用nodemon(www.npmjs.com/package/nod…) 工具,能够监听项目文件的变动,当代码被修改后,nodemon会自动帮我们重启项目,极大方便了开发和调试。
2、安装nodemon
在终端中,运行如下命令,即可将nodemon安装为全局可用的工具:
npm install -g nodemon
3、使用nodemon
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行 node app.js命令,来启用项目。这样做的坏处是:代码被修改之后,需要手动重启项目。
Express路由
2.1路由的概念
在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,格式如下:
app.METHOD(PATH,HANDLER)
Express中路由的例子:
//匹配GET请求,且请求URL为/
app.get('/',(req,res)=>{
res.send('Hello World!')
})
//匹配POST请求,且请求URL为/
app.post('/',(req,res)=>{
res.send('Got a POST request')
})
路由的匹配过程
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理
路由匹配的注意点:1、按照定义的先后顺序进行匹配
2、请求类型和请求的URL同时匹配成功,才会调用对应的处理函数
2.2 路由的使用
1、最简单的用法
在Express中使用路由最简单的方式,就是把路由挂载到app上,示例代码如下:
const express = require('express')
// 创建web服务器,命名为app
const app = express()
// 挂载路由
app.get('/',(req,res)=>{
res.send('Hello World')
})
app.post('/',(req,res)=>{
res.send('Post Request')
})
// 启动Web服务器
app.listen(80,()=>{
console.log('server running at http://127.0.0.1')
})
2、模块化路由
为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独的步骤如下:1、创建路由模块对应的.js文件
2、调用express.Router()函数创建路由对象
3、向路由对象上挂载具体的路由
4、使用module.exports向外共享路由对象
5、使用app.use()函数注册路由模块
3、创建路由模块
var express = require('express')
var router = express.Router()
router.get('/user/list',function(req,res){//挂载获取用户列表的路由
res.send('Get user list.')
})
router.post('/user/add',function(req,res){//挂载添加用户的路由
res.send('Add new user')
})
module.exports = router //向外导出路由对象
4、注册路由模块
//1、导入路由模块
const userRouter = require('./router/user.js')
//2、使用app.use()注册路由模块