Nodejs笔记
1. Node.js安装
1.1 Node.js安装
1.Node.js官网下载长期版本(LTS)安装包,选择默认安装设置,进行安装。
2.在微软终端输入node -v
回车,查看到Nodejs版本号证明安装成功。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SZk2FIN-1674715884000)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019161402032.png)]
2. 使用Nodejs运行代码及CMD命令行快捷键
2.1创建js文件
console.log('你好Node')
2.2 CMD终端运行 nodejs
通过终端打开文件保存路径,我的文件存在D:\课堂随机点名\js。
C:\Windows\system32>
cd D:\课堂随机点名\js
C:\Windows\system32>
d:
D:\课堂随机点名\js>
node hello
你好Node
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9MyMlSAL-1674715884004)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019161430279.png)]也可以在js所在的文件的地址栏直接输入cmd加回车键,快速打开cmd命令。
2.3Powershell终端运行 nodejs
在js文件路径下,通过快捷键Shift
+鼠标右键,选择通过在此处打开Powershell窗口
然后运行node hello
2.4CMD命令行快捷键
1、↑上箭头:可以自动输入之前运行过的命令
2、Tab键文件名自动补全
3、Esc键可以快速清空当前已输入的命令
4、输入命令**cls
**可以清空窗口里的内容
3. 系统模块
3.1 fs模块fs.readFile读取和fs.writeFile写入
fs模块是node官方提供用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求
例如:
- 读取文容fs.readFile(filename[, options], callback)
参数使用说明如下:
path:指定文件路径
options:(必选,否则会出问题)指定读取文件的编码格式
callback:指定文件读取后通过回调函数拿到数据结果
-
写入文件fs.writeFile(file, data[, options], callback)
参数使用说明如下:
file - 文件名或文件描述符。
data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。
options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 ‘w’
callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
3.1.1读取文件内容
// 1.导入fs模块
const fs=require('fs')
// 使用fs.readFile方法读取文件
// 参数一路径,参数二编码格式,参数三回调函数,拿到读取失败和成功的结果 err datastr
fs.readFile('../css/新建文本文档.txt','utf8',function(err,datastr){
// 读取失败会返回错误代码如:errno: -4058,
// 读取成功会返回null
console.log(err)
// 显示分割线
console.log('-------------')
// 读取失败返回 undefined
// 读取成功返回文本文件的内容
console.log(datastr)
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DmrQ6us-1674715884011)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019180543451.png)]
3.1.1.2小案例:判断是否读取成功,读取失败返回读取失败提示
const fs=require('fs')
fs.readFile('../css/新建文本文档.txt','utf8',function(err,datastr){
// 判断是否读取成功,err=true 时代表读取失败,返回读取失败信息,否则~
if(err){
return console.log('读取文件失败!'+ err.message)
}
console.log('读取文件成功,内容为:' + datastr)
})
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ScDCxi89-1674715884015)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019182339836.png)]
3.1.2写入文件内容
const fs=require('fs')
// 参数一,保存路径和文件名,参数二写入内容,参数三省略,为默认utf8格式,参数四,对调函数返回错误信息
fs.writeFile('../css/2.txt','hello.js',function(err){
//写入成功err为null
//写入失败,err则为一个错误对象
console.log(err)
})
运行结果:
console.log(err) 返回null表明执行成功
3.1.2.1小案例:读取文件后替换指定字符并写入文件
数据准备:小明=100 小红=20 小兰=34 小弟=545 笑傲=2
将以上数据写入txt文件,然后通过fs模块的readFile读取数据后,把等号替换成“:”,将空格替换为换行,然后用witeFile重新写入txt文件
const fs=require('fs')
//不写编码格式,在字符串处理时会报错
fs.readFile('../css/2.txt','utf8',function(err,str){
if (err){ console.log('读取失败!'+ err.message) }
console.log('读取成功:'+ str)
const n=str.split(' ')
const arr2=[]
n.forEach(item=> {arr2.push(item.replace('=',':'))});
const nstr=arr2.join('\r\n')
console.log('转换后的内容:' + nstr)
fs.writeFile('../css/2.txt',nstr,function(err){
if(err){
console.log('写入失败:'+ err.message)
}
console.log('写入成功')
})
})
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJTZyWJ1-1674715884018)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019202429154.png)]
3.2 path路径模块
psth模块时node提供的用来处理路径的模块。它提供了一系列方法和属性,用来满足用户对路径的处理需求。
例如:
- path.join() 拼接路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wu7LVi4H-1674715884022)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019210704870.png)]
在join中'/b/c','../'
表示c的上级目录,的结果为/b
__dirname 表示当前文件所在目录,例如: **path.join(__dirname, '../css/2.txt)
**可替代''../css/2.txt''
- path.basename() 从路径中解析文件名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJB9CwEr-1674715884064)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019210920169.png)]
- path.extname()获取文件拓展名(后缀名)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2L8SLIFe-1674715884069)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221019212113522.png)]
3.3.1小案例:解析HTML文件案例
案例目标:把嵌入式css、js从html提取出来,并添加成外联样式
// 1、导入模块
const fs=require('fs')
const path=require('path')
// 2、正则表达式匹配<style> </style>和<script> </script>
// \s是指空白,包括空格、换行、tab缩进等所有的空白,而\S刚好相反,组合就表示所有的字符
// <\/Style>反斜杠需要转义符/进行转义
const regcss = /<style>[\s\S]*<\/style>/
const regjs = /<script>[\s\S]*<\/script>/
// 3、读取文件内容,并执行三个方法
fs.readFile('../计时器.html','utf8',function(err,str){
if (err){ console.log('读取失败!'+ err.message) }
console.log('读取成功:'+ 'str')
resCSS(str)
resJS(str)
resHTML(str)
})
// 4、定义三个构造方法
// CSS构造方法
function resCSS(htmlstr){
// 正则表达式匹配resCSS(str)传递的参数
const r1=regcss.exec(htmlstr)
// 替换<Style>标签对
const r2=r1[0].replace('<style>','').replace('</style>','')
// 将处理好的结果r2写入文件夹
fs.writeFile('../css/index.css',r2,function(err){
if(err){console.log('写入失败:'+ err.message)}
console.log('写入css成功')
})
}
// JS构造方法
function resJS(htmlstr){
// 正则表达式匹配resJS(str)传递的参数
const r1=regjs.exec(htmlstr)
// 替换<Script>标签对
const r2=r1[0].replace('<script>','').replace('</script>','')
// 将处理好的结果r2写入文件夹
fs.writeFile('../css/index.js',r2,function(err){
if(err){console.log('写入失败:'+ err.message)}
console.log('写入js成功')
})
}
// HTML构造方法
function resHTML(htmlstr){
// 将内嵌标签替换为外联标签
const r2=htmlstr.replace(regcss,'<link rel="stylesheet" href="./index.css"/>')
.replace(regjs,'<script src="./index.js"></script>')
// 写入index.html
fs.writeFile('../css/index.html',r2,function(err){
if(err){console.log('写入失败:'+ err.message)}
console.log('写入html成功')
})
}
3.3 http模块学习
3.3.1 服务器相关概念介绍
http模块是node官方提供的,用来创建web服务器的模块。通过http模块提供的http.createServer()方法,就能方便的把一台普通电脑变成一台web服务器,从而对外提供web资源服务。
-
ip地址:cmd运行
ping www.baidu.com
命令,即可访问百度服务器的ip地址。 -
在开发期间,自己的电脑既是一台服务器,又是一个客户端。
127.0.0.1
或localhost
都代表本机ip地址。仅供自己测试用,无法分享给别人访问。 -
域名和域名服务器:域名是ip的别名,域名比ip方便记忆。域名转换成ip要通过域名服务器DNS解析。
-
端口号:服务器可以运行无数个web服务,每个服务都对应一个唯一的端口号。客户端发过来的请求通过端口号,可以被准确的交给对应的web服务器进行处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ChS98XOS-1674715884074)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020114700243.png)]
3.3.2 创建基本的Web服务器的方法
// 1、导入http模块
const http=require('http')
// 2、创建web服务器实例
const server=http.createServer()
// 3、为服务器绑定 request 事件,监听客户端的请求
server.on('request',function(req,res){
console.log('服务器正在被访问')
})
// 4、启动服务器
server.listen(8080,function(){
console.log('server running at http://127.0.0.1:8080')
})
在vscode中新建端口,可以直接执行以上程序,结束快捷键Ctrl+c
,清空终端输入cls
命令
3.3.2.1 req请求对象
-
req请求对象
服务器接收到客户端请求,就会调用通过
server.on()
绑定的request
事件处理函数。 -
通过以下方式可以访问客户端相关的数据和属性
req.url 是客户端请求的URL地址
req.method 是客户端请求的method类型
const http=require('http') const server=http.createServer() // req 是请求对象,包含了客户端相关的数据和属性 server.on('request', req => { // req.url 是客户端请求的URL地址 // req.method 是客户端请求的method类型 const url= req.url const method=req.method // 占位符${}是写在反引号中的,不是'' const str=`your request URL is ${url} , and request method is ${method}` console.log('服务器正在被访问:'+str) }) // ()=>{}箭头函数,等同于function(){} server.listen(8080,()=>{ console.log('server running at http://127.0.0.1') })
3.3.2.2 res响应对象
-
res响应对象,在服务器的requwst事件处理函数中,如果想访问与服务器相关的数据和属性,可以使用如下方式:
res.end()方法向客户端发送指定内容,并结束这次请求的处理过程。
const http=require('http') const server=http.createServer() server.on('request', (req,res) => { const str=`your request URL is ${req.url} , and request method is ${req.method}` console.log('服务器正在被访问:'+str) // 调用res.end()方法,向客户端响应str字符串 res.end('res:'+str) }) server.listen(8080,()=>{ console.log('server running at http://127.0.0.1') })
3.3.2.3 解决res.end()响应中文内容乱码问题
res.end()方法,向客户端发送中文,会乱码,此时需要手动设置内容的编码格式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOqRxkwx-1674715884079)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020172754873.png)]
const http=require('http') const server=http.createServer() server.on('request', (req,res) => { const str=`您请求的URL是:${req.url} , 及method类型: ${req.method}` console.log('服务器正在被访问:'+str) // 设置content-type的值为'text/html;charset=utf-8',可防止中文显示乱码 res.setHeader('content-type','text/html;charset=utf-8') res.end('res:'+str) }) server.listen(8080,()=>{ console.log('server running at http://127.0.0.1') })
3.4 自定义模块
3.4.1module.exports对象
在自定义模块中,可以使用module.exports对象,将模块的成员共享出去,宫外皆使用。
外界用require()方法导入自定义模块时,得到的就是module.ecports所指向的对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZLiT40O2-1674715884082)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020182012830.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QpSEMiJ-1674715884086)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020183023535.png)]
3.4.2 共享成员时的注意点
使用require()方法导入模块时,导入结果以module.exports指向的对象为准。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XHEasDU2-1674715884092)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020184008859.png)]
3.4.3 exports对象
exports
和module.exports指向的是同一个对象。是module.exports的代码简化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9V9jpX4-1674715884097)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020184542310.png)]
-
exports
和module.exports使用误区:**注意:**为了防止混乱,建议不要在同一个模块中同时使用exports和module.exports
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58M4tXls-1674715884102)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020190516758.png)]
3.4.4 node中的CommonJ模块规范化
node遵循CommonJS模块规范,CommonJS规定了模块的特性和各模块之间如何互相依赖。
CommonJ规定:
- 每个模块内部,
module
变量代表当前模块; - module变量是一个对象,它的
exports属性module.exports
是对外的接口 require()
加载某个模块,其实是加载该模块的module.exports属性。
3.4.5 案例:自定义格式化时间模块
// 自定义格式化时间的模块
function datay1(dtstr){
const dt=new Date(dtstr)
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}`
}
// 定义补0函数 如果值大于9直接返回,否则前面补'0'
function padZero(n){
return n > 9? n:'0'+n
}
module.exports={
datay1
}
调用自定义格式化时间的模块
const m = require('./11')
const dt=new Date()
const dd=m.datay1(dt)
console.log(dt)
console.log(dd)
执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PizY2XwJ-1674715884105)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221020200519023.png)]
4. npm与包
4.1 npm包介绍
Nodejs中第三方模块又称为包。包不同于内置的模块或自定义模块。包是第三方团队或个人开发出来的,免费供所有人使用的模块。
包检索网址:https://www.npmjs.com
包下载网址:https://www.registry.npmjs.org/ 需要npm工具才可以下载,网址无法直接访问
4.2 npm包的安装、卸载
4.2.1 安装包
安装指定包:npm install moment
或简写命令npm i moment
一次性安装多个包(包名间空格隔开):npm install [包名1] [包名2] [包名...]
安装指定包并指定版本号:npm install moment@2.22.2
或简写命令npm i moment@2.22.2
安装项目所依赖的所有包:npm install
或简写命令 npm i
//命令全写 安装moment包
//命令简写
npm install moment
npm i moment
//一次性安装多个包,包名间用空格隔开。以下命令同时安装moment,jquery,art-template这3个包。
npm i moment jquery art-template
//版本号的含义:第一位数字:大版本 第二位数字:功能版本 第三位数字:Bug修复版本
//版本号提升规则:前面的版本号增长,后面的一律归零,如2.22.2 大版本号提升结果为:3.0.0
//安装指定版本的包,通过@指定包版本号
npm i moment@2.22.2
//通过moment包,打印格式化后的当前时间
const moment = require('moment')
const dt=moment().format(`YYYY-MM-DD HH:mm:ss`)
console.log(dt)
4.2.2 卸载包
卸载包命令:npm uninstall [包名]
执行该命令卸载成功后,会自动在package.json文件中的dependencies节点中移除被卸载包的信息。
4.2.3 解决下载包速度慢的问题
淘宝npm镜像服务器:淘宝在国内搭建了一个服务器,每隔一段时间自动同步npm官方服务器上的包,这样在国内下载包速度比直接从npm官方服务器下载更快。
-
切换npm的下包镜像源
方法1:命令行直接修改
//查看当前的包下载镜像源
npm config get registry
//将下载包镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
//重新查看当前的包下载镜像源,查看切换是否成功
npm config get registry
方法2:通过nrm工具,可以快速查看和切换下zai包的镜像源
//通过npm包管理器,将nrm安装为全局可用
npm i nrm -g
//查看所有可用的镜像源,*开头的是当前使用的镜像源
nrm ls
//切换镜像源为淘宝镜像源
nrm use taobao
### 4.3 包管理的配置文件
1. 快速创建package.json文件
- npm报告管理提供了一个快捷命令 `npm init -y`,可以在执行命令时所处的目录中,快速创建package.json文件
- 快捷命令,只能在英文无空格的目录下运行,运行命令会自动记录包名和版本号。
2. 一次性安装package.json文件中的所有包
- 1`npm install`或者`npm i`命令在执行时,npm包管理工具会先读取package.json 文件中的dependencies节点中包名和版本号,然后一次性下载。
3. dependencies节点
- package.json文件中的dependencies节点,专门用来记录npm install命令安装了哪些包。
- 项目在开发阶段和项目上线后都需要用的包,建议记录在dependencies节点
4. devDependencies节点
- 仅在项目开发阶段使用的包,可以记录在devDependencies节点中
- 安装指定的包,并记录在devDependencies节点中
在终端执行命令:`npm install [包名] --save-dev` 或简写命令**`npm i [包名] -D`**
该命令包名和--save-dev 或者-D的顺序可以颠倒,npm i -D [包名],这样也可以运行
### 4.4 包的分类
被安装在项目的node_modules目录中的包都是项目包,项目包又分为开发依赖包、核心依赖包、全局包;
- 开发依赖包:记录在devDependencies节点中的包,仅开发阶段会用到的包
- 核心依赖包:记录在dependencies节点中的包,开发阶段和上线后都会使用的包
//开发依赖包:记录在devDependencies节点中,
npm i 包名 -D
//核心依赖包:记录在dependencies节点中
npm i 包名
- 全局安装包:执行命令时提供-g参数。
- 仅工具性质的包才有全局安装的必要,因为它们提供了好用的终端命令
- 可以参考官方文档使用说明,来判断该包是否有必要全局安装
- 全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_modules目录
//全局安装nrm包
npm i nrm -g
//卸载全局安装的nrm包
npm uninstall nrm -g
i5ting_toc:该包可以把md文档转为html页面的工具,使用步骤如下:
//1、全局安装
npm install i5ting_toc -g
//2、终端中运行命令,-o表示转换成功后用默认浏览器打开html文件
i5ting_toc -f md文件路径 -o
### 4.5 开发属于自己的包
#### 4.5.1 包结构规范
一个规范的包结构上必须符合以下三个点:
1. 包必选包含单独的目录而存在
2. 报的顶级目录下必须包含package.json这个包管理配置文件
3. package.json中必选包含属性:name 包名,version 版本号,main 包入口
#### 4.5.2 初始化报的基本结构
1. 新建iset-time-html文件夹作为包的根目录
2. 在iset-time-html文件夹新建如下三个文件
- packeag.json (包管理配置文件)
```json
{
"name":"iset-time-html",
"version":"1.0.0",
"main":"index.js",
"description": "提供了时间格式化、html特殊标签转换相关功能",
"keywords":["time","html"],
"license":"ISC"
// license许可协议相关内容参考:https://www.jianshu.com/p/86251523e898
}
- index.js (包的入口文件)
1. index.js中设置时间格式化的方法
```js
// 自定义格式化时间的方法
function datay1(dtstr){
const dt=new Date(dtstr)
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 }// 定义补0函数 如果值大于9直接返回,否则前面补'0'
module.exports={datay1}//向外暴露需要的成员
```
调用index.js进行测试函数是否有效
```js
const ith = require('../iset-time-html/index.js')
const dt=ith.datay1(new Date())
console.log(dt)
```
2. index.js设置定义转义与反转义html的方法
```js
//定义转义html字符的函数的
return str.replace(/<|>|"|&/g,(match)=>{
switch (match){
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
```
```js
//定义反转义html字符的函数的
function HstrF(str){
return str.replace(/<|>|"|&/g,(match)=>{
switch (match){
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
```
调用Hstr HstrF或者方法,记得在module.exports中要添加该接口{datay1,Hstr,HstrF}
```js
const ith = require('../iset-time-html/index.js')
const s='<h1 style=" ">点名系统 </h1>'
console.log(ith.Hstr(s))
//运行结果为<h1 style=" ">点名系统&nbsp;</h1>
3. 将不同的功能进行模块化
首先把时间格式化和html转义函数别保存在一个目录,然后在index.js文件中写上包的入口文件和需要向外暴漏的成员。
下例中时间格式化和html转义函数分别保存在包的根目录下的src文件夹(/iset-time-html/src)dateFormat.js | htmlescape.js,然后在index.js文件中写入包的入口信息及需要向外暴露的成员。
```js
//index.js文件中
// 这是包的入口文件
const date=require(./src/dateFormat)
const escape=require(./src/htmlescape)
//向外暴露需要的成员
//...date是展开date变量的意思
module.exports={
...date,
...escape
}
```
- REANME.md (文档资料)
````markdown
## 安装
```
npm i iset-time-html
```
## 导入
```js
const ith = require('../iset-time-html/index.js')
```
## 格式化时间
```js
// 调用datay1对时间格式化
const dt=ith.datay1(new Date())
console.log(dt)
```
## 转义HTML中的特殊字符
```js
// HTML字符串
const s='<h1 style="text-align:center">点名系统<span>123 </span></h1>'
const f='<h1 style="text-align:center">点名系统<span>123&nbsp;</span></h1>'
// 打印转义方法Hstr处理后的html内容,转换后结果为f的内容
console.log(ith.Hstr(s))
// 打印反转义方法HstrF处理后的html内容,转换后结果为s的内容
console.log(ith.HstrF(f))
```
## 开源协议
ISC
### 4.6 发布包
1. 注册npm账号,邮箱链接验证成功后即可。
2. 在终端执行**`npm login `**命令,按照提示依次输入用户名 、密码 、邮箱后回车 登录npm账号。
**注意:**必选确保下包服务器地址为npm官方服务器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6NmtQMWb-1674715884110)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221021153230983.png)]
3. 将终端切换到包的根目录。运行`npm publish`命令,即可把包发布到npm上。
**注意:**包名不能与npm服务器里现有的包重名。
4. 可以浏览器登录https://www.npmjs.com ,选择用户下拉菜单里的pachages查看自己上传的包信息。
**删除已发布的包,终端执行命令`npm unpublish [包名] --force` 即可从npm删除已发布的包。**
### 4.7 npm命令的使用
1. 包的初始化
```bash
npm init -y //这里文件夹名字不能有 中文 空格 纯数字
// 快速创建 package.json文件,里面会记录npm包管理的名称和版本号
- 安装包
npm i 包名
npm install 包名
npm i 包名@版本号 => npm i jquery@2.22.2
- 安装全局包
npm i 包名 -g
- 卸载包
npm uninstall 包名
npm uninstall 包名@版本号
npm uninstall 包名 -g # 卸载全局包
- devDependencies 节点
# 用于记录项目上线之后不会用到的包
npm i 包名 -D //简写方式
npm install 包名 --save-dev
- 解决包下载慢的问题
# 淘宝 NPM 镜像服务器
# 查看当前的下包镜像源
npm config get registry
#设置为淘宝镜像(切换镜像)
npm config set registry https://registry.npm.taobao.org
#检查是否设置成功
npm config get registry
- nrm的使用
1. npm i nrm -g # 安装nrm
2. nrm ls # 展示可用的镜像链接
3. nrm use 镜像名字
- 包的发布
1. nrm use npm #切换为官方镜像
2. npm login #登陆输入账号和密码
3. npm publish #注意终端要在项目的根目录
4. npm unpublish 包名 --force #删除已发布的包
- 安装nodemo的使用
npm i nodemon -g # nodemon+项目文件的相对路径启动运行服务器,如:nodemon hello.js
5. 模块的加载机制
5.1优先从缓存加载
代码在第一次加载后会被缓存,多次调用require()不会导致模块的代码被多次执行。
内置模块,用户自定义模块,第三方模块,都会优先从缓存加载,以提高模块的加载效率。
5.2内置模块的加载机制
内置模块的加载优先级最高
例如,第三方模块和内置模块同名都叫fs,require(‘fs’)始终返回内置模块
5.3 自定义模块的加载机制
require()自定义模块是=时,必须指定路径标识符 ./ or …/,否则会被node当作内置或者第三方模块加载
require()导入时,省略了文件拓展名,node将按顺序加载以下文件:
- 按照确切的文件名进行加载
- 按照.js后缀进行加载
- 按照.json后缀进行加载
- 按照.node后缀进行加载
- 加载失败,终端报错
5.4 第三方模块的加载机制
require()不包含路径标识符,且不是内置模块,node将会从/node_modules文件夹中加载第三方模块。如果没有找到对应模块,将返回上级模块查找,直到文件系统根目录。
5.5 目录作为模块时的加载机制
require(‘…/iset-time-html/’),将目录作为参数时,有三种情况:
- 该目录下有packeag.json文件,以main属性作为加载入口
- 该目录没有packeag.json文件,或main属性无效,node将会加载该目录下的index.js文件
- 以上两步都失败,终端报错信息 Error:Cannot find module ‘XXXX’
6. Express
6.1 初始Express
Express是基于node.js平台,快速,开放,极简的web开发框架。是专门用来创建web服务器的npm包,可以使我们快速创建web服务器和API接口服务器。
Express基于node.js内置的http模块进一步封装,比http模块更简洁,可以极高的提高开发效率。
Express中文官网:http://www.expressjs.com.cn/
6.1.1 安装及基本使用
项目目录中npm i express@4.17.1
执行该终端命令即可安装express到项目中
- 创建服务器
// 1.导入express
const express= require('express')
// 2.创建web服务器
const app = express()
// 3.启动web服务器
app.listen(80,()=>{console.log('服务器已启动')})
-
监听客户端的get和post请求,并向客户端响应具体内容
const express= require('express') const app = express() // 监听客户端的get和post请求,并向客户端响应具体内容 app.get('/user',(req,res)=>{ // 调用express提供的res.send方法,向客户端相应一个json对象 res.send({name:'二狗子',age:'20',gender:'男'}) }) app.post('/user',(req,res)=>{ //调用express提供的res.send方法,向客户端相应一个字符串 res.send('请求成功') }) app.listen(80,()=>{console.log('服务器已启动')})
-
req.query对象可以获取url中携带的查询参数
req.query对象可以访问客户端通过http://127.0.0.1/?name=xm&age=20“查询字符串”的形式,发送到服务器的参数
const express= require('express') const app = express() app.get('/user',(req,res)=>{ res.send({name:'二狗子',age:20,gender:'男'}) }) app.post('/user',(req,res)=>{ res.send('请求成功') }) app.get('/',(req,res)=>{ // 通过req.query可以获取客户端发过来的查询参数 // 默认情况下req.query是一个空对象 console.log(req.query) res.send(req.query) }) app.listen(80,()=>{console.log('服务器已启动')})
-
req.params对象获取URL中的动态参数
req.params对象可以访问URL中通过 : 匹配到的动态参数。
:后面的名字根据需要写,URL中可以包含多个动态参数如:
app.get('/user/:id:name',(req,res)=>{ })
const express= require('express') const app = express() app.get('/user/:id',(req,res)=>{ //req.params是动态匹配到的URL参数,默认是一个空对象 console.log(req.params) res.send(req.params) }) app.listen(80,()=>{console.log('服务器已启动')})
6.1.2 express.static()方法,快速对外提供静态资源
const express= require('express')
const app = express()
// express.static()函数,快速对外提供静态资源(html,css,js,txt)
// 敲黑板,路径css必须与js在同一级目录下才有效,否则无法向客户端提供静态资源
//网址后拼接文件名加后缀就可以访问,http://127.0.0.1/index.html
//托管多个静态资源目录,即多次调用express.static()函数,以下为托管css和js两个资源目录
app.use(express.static('./css'))
app.use(express.static('./js'))
app.listen(80,()=>{console.log('服务器已启动')})
//express.static()方法挂载路径前缀
const express= require('express')
const app = express()
//网址 + 路径前缀 +带后缀的文件名
//http://127.0.0.1/abc/index.html
app.use('/abc',express.static('./css'))
app.listen(80,()=>{console.log('服务器已启动')})
6.2 Express路由
6.2.1 Express路由介绍
Express中路由是指客户端请求和服务器函数之间的映射关系。 Express终端路由分三部分:请求类型、url地址、处理函数。
//app.METHOD(path,handler)
//METHOD/请求类型可以是get或者post
//path/url地址
//handler/处理函数
//匹配GET请求,请求的URL为/,处理函数打印一句话
app.get('/',function(){consloe.log(这是GET类型的请求)})
//匹配post请求,请求的URL为/,处理函数打印一句话
app.post('/',function(){consloe.log(这是POST类型的请求)})
app.listen(80,()=>{console.log('服务器已启动')})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkhCfwVm-1674715884114)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221022114827319.png)]
//Express路由的最简单用法
const express= require('express')
const app = express()
//挂载路由
app.get('/',(req,res)=>{res.send('哈喽这是 GET 类型的请求')})
app.post('/',(req,res)=>{res.send('哈喽这是 POST 类型的请求')})
app.listen(80,()=>{console.log('服务器已启动')})
6.2.2 Express路由的使用
为了提高效率路由很少会挂载到app上,推荐将路由抽离为单独的模块,步骤如下:
-
创建路由模块对应的.js文件
-
调用express.Router()函数创建路由对象
-
向路由对象挂载具体路由
-
使用module.exports()向外共享路由对象
// #1.创建路由 自定义路由模块 # // 2.调用express.Router()函数创建路由对象 const express= require('express') const router = express.Router() // 3.挂载具体路由 router.get('/list',(req,res)=>{res.send('get user list-- 获取用户')}) router.post('/add',(req,res)=>{res.send('add new user--添加用户成功')}) // 4.向外共享路由对象 module.exports=router
-
使用app.use()函数注册路由模块
const express= require('express') const app = express() // 1.导入自定义路由模块 const router = require('./11') // 2.注册路由模块 app.use(router) //为路由模块添加访问前缀的方法 app.use('/bac',router) app.listen(80,()=>{console.log('服务器已启动')})
6.3 Express中间件
Express中间件:值业务流程的中间处理环节。中间件可以共享一份req,res,在上游为req,res设置自定义方法和属性,可供下游的中间件和路由使用。
next()函数是多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
6.3.1 局部中间件
const express= require('express')
const app = express()
//局部中间件函数
const m1 = function(req,res,next){
console.log('局部生效的第一个中间件函数')
next()
}
app.get('/',m1,(req,res)=>{
res.send('局部中间件演示效果')
})
app.listen(80,()=>{console.log('服务器已启动')})
6.3.2 使用多个局部中间件
//局部中间件函数
const m1 = function(req,res,next){
console.log('局部生效的第一个中间件函数')
next()
}
const m2 = function(req,res,next){
console.log('第2个局部中间件函数')
next()
}
//m1,m2 也可以写成[m1,m2],效果一样
app.get('/',m1,m2,(req,res)=>{
res.send('局部中间件演示效果')
})
app.get('/user',(req,res)=>{
res.send('user page.')
})
6.3.3 全局中间件的作用
在上游为req,res 设置自定义方法和属性,可供下游的中间件和路由使用
const express= require('express')
const app = express()
app.use((req,res,next)=>{
//获取请求到服务器的时间
//为req对象挂载自定义属性time,供res.send响应到客户端
const time=Date.now()
req.startTime=time
console.log('这是最简单的中间件函数')
next()
})
app.get('/',(req,res)=>{
res.send('请求到服务器的时间:' + req.startTime)
})
app.listen(80,()=>{console.log('服务器已启动')})
6.3.4 使用多个全局中间件
可以使用app.use()连续定义多个全局中间件,执行顺序为定义的先后顺序进行调用。
//console.log会按照顺序1、2、打印内容,并且返回'用时:Time' 到客户端
app.use((req,res,next)=>{
req.startTime=time
console.log('1、调用第一个全局中间件')
next()
})
app.use((req,res,next)=>{
console.log('2、调用第二个全局中间件')
next()
})
app.get('/',(req,res)=>{
res.send('用时:' + req.startTime)
})
6.3.5 中间件的4个注意事项
- 必须在路由之前注册中间件
- 客户端请求,可以连续调用多个中间件处理
- function中必须要有next(),且必须是function语句的末尾
- 连续调用多个中间件,req,res对象是共享的
6.3.6 中间件的分类
-
应用级别的中间件
绑定到app实例上的中间件叫做应用及中间件,包括全局中间件、局部中间件;
全局中间件:app.use()
局部中间件:app.get() app.post()
-
路由级别的中间件
绑定在express.Router()上的中间件叫路由中间件
const express= require('express') const app = express() const router = require('./11') // 路由级别的中间件 router.use((req,res)=>{ console.log('时间:'+ Date.now()) next() }) app.use('/',router) app.listen(80,()=>{console.log('服务器已启动')})
-
错误级别的中间件
错误级别的中间件必须在所有路由之后
app.get('/',(req,res)=>{ throw new Error('未知错误!')//认为制造一个Error错误信息 res.send('Home page') }) //定义错误级别的中间件 app.use((err,req,res,next)=>{ console.log('发生错误:'+ err.message) res.send('Error:'+ err.message) next() })
-
Express内置中间件
- express.static 快速托管静态资源:HTMML,CSS,JS,图片,txt等(无兼容性)
app.use(express.static({需要托管的资源目录如:'./index'}))
- express.json 解析json格式的请求体数据(有兼容性,仅支持4.16.0以上版本)
req.body 可以获取客户端发送的Json和URL—encoded格式的数据
不配置解析表单数据的中间件,req.body 默认为undefined
//配置json中间件,解析Json格式的数据 app.use(express.json()) //post路由 app.post('/',(req,res)=>{ console.log(req.body) })
- express.urlencoded 解析URL—encoded格式的请求体数据(有兼容性,仅支持4.16.0以上版本)
//配置express.urlencoded ,解析URL—encoded格式的数据 app.use(express.urlencoded({extended:false})) app.post('/',(req,res)=>{ console.log(req.body) })
-
第三方中间件
第三方包工具安装,看说明使用即可
6.3.7 自定义中间件
6.37.1 监听req的data事件
在中间件中,需要监听req的data事件,获取客户端发送到服务端的数据,若数据量较大,通常客户端会把数据切割后分期发送到服务端,所以data事件可能会多次被触发。每次出发都会收到一部分数据。需要手动拼接。
const express= require('express')
const app = express()
const router = require('./11')
// 路由级别的中间件
app.use((req,res,next)=>{
let str=''
req.on('data',(chunk)=>{str+=chunk})
next()
})
app.listen(80,()=>{console.log('服务器已启动')})
6.37.2 监听req的end事件
当请求体数据接收完毕后,会自动触发end事件,
因此我们可以在req的end事件中,拿到并处理完整的请求体数据。
const express= require('express')
const app = express()
//2.导入querystring解析请求体模块
const qs =require('querystring')
app.use((req,res,next)=>{
let str = ''
//监听req的data事件
req.on('data',(chunk)=>{
str += chunk
console.log('data:'+ str)
})
//1.通过end事件,拿到完整的请求体数据,为了解决乱码问题,使用querystring的parss方法解析请求体
req.on('end',()=>{
//3.querystring解析请求体
const sstr= qs.parse(str)
//为req挂载自定义body属性,这样下游就可以使用sstr常量的数据
req.body=sstr
console.log(sstr )
next()
})
})
app.post('/',(req,res)=>{
//调用req.body属性
console.log(req.body)
res.send(req.body)
})
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
6.37.2 将中间件封装为独立的模块
为了优化代码结构,我们可以将自定义的中间件函数,封装为独立的模块
//1.创建独立的js文件,本例取名body.js
const qs =require('querystring')
const body =(req,res,next)=>{
let str = ''
req.on('data',(chunk)=>{
str += chunk
console.log('data:'+ str)
})
req.on('end',()=>{
const sstr= qs.parse(str)
req.body=sstr
console.log(sstr )
next()
})
}
module.exports=body
优化后的代码结构就非常简洁
const express= require('express')
const app = express()
//2.导入自定义模块
const body =require('./body.js')
app.use(body)
app.post('/',(req,res)=>{
console.log(req.body)
res.send(req.body)
})
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
6.4 使用Express写接口
6.4.1 创建API路由模块
//1.创建基础服务篇
const express= require('express')
const app = express()
//app.use(express.json())
app.use(express.urlencoded({extended:false}))//解析需要放在在路由之前
//3.导入路由模块
const router= require('./router.js')
//4.创建API路由接口为api
//http://127.0.0.1/api/get 网址需要加上/api/get,因为路由模块中定义了/get
app.usr('/api',router)
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
//2.创建API路由模块,文件名router.js
const express= require('express')
const router = express.Router()
router.get('/get',(req,res)=>{
//通过req.query属性 获取客户端通过查询字符串发送到服务器的数据
const query=req.query
res.send({
status:0, //0表示处理成功,1表示失败
msg:'GET请求成功',
data:query //需要响应客户端的数据
})
console.log(query)
})
router.post('/post',(req,res)=>{
//通过req.body属性 获取请求体包含的URL-encoded格式的数据
const body=req.body
res.send({
status:0, //0表示处理成功,1表示失败
msg:'post请求成功',
data: body//需要响应客户端的数据
})
console.log(body)
})
router.delete('/delete',(req,res)=>{
res.send({
status:0,
msg:'post请求成功'
})
console.log('delete')
})
module.exports=router
**注意:**如果接口定义的是http协议,而打开网页使用的是file:打开网页,网站提示如下错误,就表示存在接口跨域问题。
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1/api/get?name=%E5%B0%8F%E6%98%8E&shenf=%E5%AD%A6%E7%94%9F 的远程资源。(原因:CORS 头缺少 ‘Access-Control-Allow-Origin’)。状态码:200
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='UTF-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>接口跨域测试</title>
<script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
<button id="g">GET</button>
<button id="p">POST </button>
<button id="d">delete</button>
</body>
<script>
$(function(){
// 为GET按钮绑定点击事件处理函数
$('#g').on('click',function(){
$.ajax({
type:'GET',
url:'http://127.0.0.1/api/get',
data:{name:'小明',shenf:'学生'},
success:function(res){
console.log(res)
},
})
})
// 为POST按钮绑定点击事件处理函数
$('#p').on('click',function(){
$.ajax({
type:'POST',
url:'http://127.0.0.1/api/post',
data:{bookname:'水浒传',zuozhe:'施耐庵'},
success:function(res){
console.log(res)
},
})
})
// 为DELETE按钮绑定点击事件处理函数
$('#d').on('click',function(){
$.ajax({
type:'DELETE',
url:'http://127.0.0.1/api/delete',
success:function(res){
console.log(res)
},
})
})
})
</script>
</html>
6.5 通过CORS解决API路由接口的跨域问题
6.5.1 API接口的跨域问题
在线版 jquery 工具:Staticfile CDN
cors是Express的一个第三方中间件。通过安装和配置cors中间件,可以方便的处理接口的跨域问题。
使用步骤:
- 运行
npm i cors
安装 - 使用
const cors= require('cors')
导入 - 在路由之前调用
app.use(cors())
进行配置
const express= require('express')
const app = express()
// 通过导入并配置CORS,解决API路由接口的跨域问题
const cors= require('cors')
app.use(cors())
app.use(express.urlencoded({extended:false}))//需要放在在路由之前
const body =require('./router.js')
app.use('/api',body)
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
6.5.2 cors跨域资源共享之响应头部
cors跨域资源共享之响应头部:
- Access-Control-allow-Origin
Origin参数指定允许访问该资源的URL,如:
//只允许来自http://itcast.cn的请求
res.setHeader('Access-Control-allow-Origin','http://itcast.cn')
-
Access-Control-allow-headers
CORS 默认仅支持9 个请求头:
- Accept
- Accept-LanguageC
- ontent-Language
- DPR
- DownlinkSa
- ve-Data
- Viewport-Width
- Width
- Content-Type
-
1 值仅限于下列三个中的一个:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers 对额外 的请求头进行声明,否则这次请求会失败!
// 允许客户端额外发送向服务器发送 Content-Type 请求头和 X-Custom-Header 请求头
// 多个请求头之间使用英文的逗号进行分割
res.setHeader(' Access-Control-Allow-Headers','Content-Type , X-Custom-Header')
-
Access-Control-allow-Methods
CORS 默认仅支持GET、POST、HEAD 请求。
PUT、DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods 来指明实际请求所允许使用的 HTTP 方法。
示例代码如下// 只允许 POST,GET,DELETE,HEAD 请求方法 //* 是允许所有的 HTTP请求方法 res.setHeader('Access-Control-Allow-Methods', 'POST,GET,DELETE,HEAD')
根据请求方式和请求头的不同,可以将CORS请求分为简单请求和预检请求。
6.4.3 简单请求
同时满足以下两大条件的请求,就属于简单请求:
-
请求方式:GET、POST、HEAD 三者之一
-
HTTP 头部信息不超过以下几种字段:
无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、 Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-formurlencoded、multipart/form-data、text/plain)
简单请求的特点:客户端与服务器之间只会发生一次请求。
6.4.4 预检请求
只要符合以下任何一个条件的请求(非简单请求就是预检请求),都需要进行预检请求:
- 请求方式为 GET、POST、HEAD 之外的请求Method 类型
- 请求头中包含自定义头部字段
- 向服务器发送了application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一 次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
预检请求的特点:OPTION 预检请求成功之后,才会发起真正的请求。
6.6 JSONP的概念和特点
由于浏览器的安全性限制,不允许 AJAX 访问协议不同、域名不同、端口号不同的数据接口,浏览器认为这种访问不安全。为了解决这种跨域限制的问题,可以通过动态创建 script 标签的形式,把 script 标签的 src 属性,指向数据接口的地址,因为 script 标签不存在跨域限制,这种数据获取方式,称作 JSONP
JSONP只支持GET请求
6.6.1 JSONP接口
如果项目中已经配置了CORSS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明josnp接口,否则会被处理成cors接口。
//
const express= require('express')
const app = express()
app.use(express.urlencoded({extended:false}))//需要放在在路由之前
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// 定义 JSONP 接口具体的实现过程
// 1. 得到客户端发送的回调函数的名称
const funcName = req.query.callback
// 2. 定义要发送到客户端的数据对象
const data = { name: '小明', age: 11 ,sex:0}
// 3. 拼接出一个函数的调用,JSON.stringify函数把data数据转换成json格式的字符串
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,响应给客户端
res.send(scriptStr)
})
const cors= require('cors')
app.use(cors())
const body =require('./11')
app.use('/api',body)
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1/')})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='UTF-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>接口跨域测试</title>
<script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
<button id="g">GET</button>
<button id="p">POST </button>
<button id="d">delete</button>
<button id="jsonp">JSONP</button>
</body>
<script>
$(function(){
// 为GET按钮绑定点击事件处理函数
$('#g').on('click',function(){
$.ajax({
type:'GET',
url:'http://127.0.0.1/api/get',
data:{name:'小明',shenf:'学生'},
success:function(res){
console.log(res)
},
})
})
// 为测试POST按钮绑定点击事件处理函数
$('#p').on('click',function(){
$.ajax({
type:'POST',
url:'http://127.0.0.1/api/post',
data:{bookname:'水浒传',zuozhe:'施耐庵'},
success:function(res){
console.log(res)
},
})
})
// 为delete按钮绑定点击事件处理函数
$('#d').on('click',function(){
$.ajax({
type:'DELETE',
url:'http://127.0.0.1/api/delete',
success:function(res){
console.log(res)
},
})
})
$('#jsonp').on('click',function(){
$.ajax({
type:'get',
url:'http://127.0.0.1/api/jsonp',
dataType:'jsonp',
success:function(res){
console.log(res)
},
})
})
})
</script>
</html>
7. Mysql数据库
7.1 数据库概念
数据库:用来组织,存储,管理数据的仓库
数据库管理系统:用户可以对数据库中的数据进行增、删、改、查操作的操作软件。
关系型数据库(又叫传统型数据库,或者SQL数据库):Mysql、Oracie、 SQL server
非关系型数据库(又叫新型数据库,no SQL数据库):Mongodb
Community(支持社区版)的数据库:Mysql、Mongodb
关系型数据库组织结构是由: database(数据库)、table(表)、row(数据行)、field(字段)四部分组成,和Excel的工作簿、工作表,行,列概念一样
7.2 安装并配置Mysql
前端进阶教程node.js入门到精通.Windows10系统安装MySQL
7.3 Mysql的基本使用
前端进阶教程node.js入门到精通.MySQL Workbench组成部分
DataTypr:
-
int 整数
-
VARCHAR(45) 字符串 limit45位
-
tinyint 布尔值
字段的约束标识:
- PK(Primary key)主键
- NN(Not Null)不允许空值
- UQ(Unique)唯一值
- Default/Expression 默认值
- UN(unsigned )整数
- AI(Auto Increment)值自动增长
- G(generated)自动生成
- ZF(zero fill) 值中最有意义的字节总为0,并且不保存
- B(binary) 二进制
-- #查询内容;
SELECT * FROM users;
SELECT id AS'序号' ,name AS'姓名',password AS '密码',status AS '状态' FROM users
SELECT * FROM users WHERE status=0;
SELECT id,name FROM users;
SELECT id,name FROM my_db_01.users;
-- #指定表内插入新数据;
INSERT INTO users VALUES( 9,'s11ll','adkfj','1');
INSERT INTO users(name,password) VALUES('AAS','ADSDFSDFF');
-- #更新语句;
UPDATE users SET status='1' WHERE id!=0;
-- ORDER BY 对结果进行排序 参数:降序为DESC。默认值为ASC升序
SELECT * FROM users ORDER BY id DESC;
-- 多重排序
SELECT * FROM users ORDER BY status DESC, name asc;
-- SQL数字函数之COUNT
SELECT COUNT(*)as'统计' FROM users WHERE status=0;
-- #删除表内指定数据:
DELETE FROM users WHERE id=7;
-- #比较运算符:
等号:=
小于:<
大于:>
小于等于:<=
大于等于:>=
不等于:<> or !=
-- 逻辑运算符:
AND 与
OR 或
NOT 非
XOR 异或
7.4 在Express中操作Mysql
-
安装第三方模块Mysql
//npm安装Mysql模块 npm install mysql
-
通过Mysql模块连接到数据库
// 配置MySQL模块 // 1.导入Mysql模块 const mysql=require('mysql') // 2.建立数据连接 const db=mysql.createPool({ host:'127.0.0.1', //指定IP user:'root', //指定账号 password:'root', //指定密码 database:'my_db_01' //指定要操作的数据库 }) // 测试mysql 能否正常工作 db.query('select 1',(err,results)=>{ // 执行失败时报错 if(err) return console.log(err.message) // sql语句执行成功 console.log(results) })
通过node执行以上代码命令:
node .\mysql.js
-
通过Mysql模块执行SQLl语句
- 查询语句select * from
const mysql=require('mysql') const db=mysql.createPool({ host:'127.0.0.1', user:'root', password:'root', database:'my_db_01', }) // 查询表中所有的数据 const sqlstr='select * from users' db.query(sqlstr,(err,results)=>{ if(err) return console.log(err.message) console.log(results) //执行查询语句,返回的是数组
- 插入数据语句:INSERT INTO
const mysql=require('mysql') const db=mysql.createPool({ host:'127.0.0.1', user:'root', password:'root', database:'my_db_01', }) const user= {name:'小花',password:'123'} // 定义待执行的SQL语句, SQL语句中可以使用?占位符进行展位 const sqlstr='INSERT INTO users(name,password) VALUES(?,?)' // 执行SQL语句 db.query(sqlstr,[user.name,user.password],(err,results)=>{ if(err) return console.log(err.message) // 判断插是否入成功,执行INSERT INTO语句,results则是一个对象,可以使用results.affectedRows属性,判断是否插入成功 if(results.affectedRows ===1 ){console.log('数据插入成功')} })
// 插入数据的便捷方式 const user= {name:'小花1',password:'123'} const sqlstr='INSERT INTO users SET ?' db.query(sqlstr,user,(err,results)=>{ if(err) return console.log(err.message) if(results.affectedRows ===1 ){console.log('数据插入成功')} })
- 更新数据:UPDATE users SET
const mysql=require('mysql') const db=mysql.createPool({ host:'127.0.0.1', user:'root', password:'root', database:'my_db_01', }) const user= {id:16,name:'小花哗啦啦',password:'000000'} const sqlstr='UPDATE users SET name=?,password=? WHERE id=?' db.query(sqlstr,[user.name,user.password,user.id],(err,results)=>{ if(err) return console.log(err.message) if(results.affectedRows ===1 ){console.log('数据插入成功')} })
// 更新数据的便捷方式 const user= {id:16,name:'啦啦',password:'111111'} const sqlstr='UPDATE users SET ? WHERE id=?' db.query(sqlstr,[user,user.id],(err,results)=>{ if(err) return console.log(err.message) if(results.affectedRows ===1 ){console.log('数据插入成功')} })
-
删除数据语句:
const sqlstr='DELETE FROM users WHERE id=?' db.query(sqlstr,16,(err,results)=>{ if(err) return console.log(err.message) if(results.affectedRows ===1 ){console.log('数据插入成功')} })
// 标记删除,指定一个字段,通过0和1表示删除与否,比直接删除更安全, const sqlstr='UPDATE users SET status=? WHERE id=?' db.query(sqlstr,[1,9],(err,results)=>{ if(err) return console.log(err.message) if(results.affectedRows ===1 ){console.log('标记删除成功')} })
8. 前后端的身份认证
8.1 目前主流的Web开发模式
8.1.1 基于服务端渲染的传统Web开发模式
服务器发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。客户端不需要使用Ajax技术额外请求页面数据。示例代码如下:
app.get('index.html',(req,res)=>{
const user={name:'zs',age='10'}
const html='<h1>姓名:${user.name},年龄:${user.age}</h1>'
res.send(html)
})
优点:
- 前端耗时少。浏览器只负责渲染,更节能。
- 有利于SEO。客户端接收的是完整的HTML内容,方便爬虫获取信息
缺点:
-
占用服务器资源,造成服务器的访问压力。
-
不利于前后端分离,开发效率低。
8.1.2 基于前端渲染的新型Web开发模式
前后端分离的开发模式,依赖于Ajax技术的广泛应用。后端只负责提供API接口,前端使用Ajax调用接口的模式
优点:
- 开发体验好。前端专注于UI页面的开发,后端专注于API的开发,且前端有更多的选择性。
- 用户体验好。Ajax技术可以实现页面的局部刷新,极大地提高了用户的体验。
- 减轻了服务器的渲染压力。
缺点:
- 不利于SEO。html内容需要在客户端拼接,所以爬虫无法获取完整有效内容。(通过Vue、Reaxt等前端框架的SSR(sever side render)技术能够很好的解决SEO问题)
7.5.1.3 如何选择Web开发模式
服务端渲染:需要良好的SEO,没有复杂的交互。
前端渲染:交互性强,不考虑SEO,如:后台管理系统。
服务端渲染+前端渲染:根据实际需求而定,如:首页服务端渲染+其他页面前端渲染。
8.2 身份认证
身份认证又称为身份验证,鉴权,是通过一定的手段对用户身份的确认。如:邮箱密码验证,手机验证码验证,二维码验证等。
开发模式不同认证方案也不一样:
- 服务端渲染推荐Session认证机制
- 前端渲染推荐使用JWT认证机制
8.2.1 Session认证机制
- HTTP协议的无状态性
指客户端每次HTTP请求都是独立的,连续多个请求之间没有直接关系,服务器不会主动保留每次HTTP请求的状态。
- 如何突破HTTP无状态的限制
Cookie是存储在用户浏览器中的一段不超过4KB的字符串,它是由Name,Value和他他几个用于控制cookie有效期,安全性,适用范围的可选属性组成。
不同域名下的Cookie是各自独立的,每当客户端发起请求是,会自动把当前域名下所有未过期的Cookie一起发送到服务器。
Cookie特性:
-
自动发送
-
域名独立
-
4KB限制
-
存在有效期
- 在身份认证中的作用
客户端在第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的Cookie,客户的自动将Cookie保存在浏览器中。
随后,客户每次请求服务器时,浏览器都会自动将身份认证相关的Cookie,通过请求头的形式发送给服务器,服务器即可验名客户端的身份。
- Cookie没有安全性密码等身份信息禁止存放在Cookie
由于Cookie是存储在浏览器中的,而且浏览器提供了读写Cookie的API,因此Cookie很容易被伪造。所以不建议服务器将重要的隐私数据通过Cookie形式发送到浏览器。
8.2.2 在Express中使用Session
- 配置express-session中间件
express-session中间件安装成功后,需要通过app.use()来注册session中间件,代码如下:
const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
secret:'keyboard cat', //该属性的值可以为任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
-
向session中存储数据
express-session中间件配置成功后,就能通过req.session来访问session对象,从而存储用户的关键信息;
const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
secret:'keyboard cat', //该属性的值可以为任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
// 登录API接口
app.post('/api/login',(req,res)=>{
// 判断用户提交的登录信息是否正确
if(req.body.username !=='admin' || req.body.password !== '000000'){
return res.send({sratus: 1,msg:'登录失败'})
}
// 02:将登录成功后的信息保存到Session中
// 只有配置session中间件后req.session属性才有效
req.session.user=req.body //用户信息
req.session.islogin=true //用户登陆状态
res.send({status:0,msg:'登陆成功'})
})
// 获取用户名的接口
app.get('/api/username',(req,res)=>{
// 03: 从session中获取用户名称,响应给客户端
if(!req.session.islogin){
return res.send({status:1,msg:'fail'})
}
res.send({
status:0,
msg:'success',
username: req.session.user.username,
})
// 退出登录接口
app.post('/api/logout',(req,res)=>{
// 04:清空session信息
// req.session.destroy()函数仅清空当前用户的session信息
req.session.destroy()
res.send({status:0,msg:'退出登录成功',
})
})
// 调用app.listen方法,指定端口号,并启动服务器
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1:80')})
})
8.2.3 JWT认证原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTTGZAyb-1674715884119)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114204018721.png)]
总结:用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。
JWT的组成部分
JWT通常由三部分组成:分别是Header(头部)、Payload(有效荷载)、Signature(签名)。
三者之间使用英文的“.”分隔,格式如下:
Header.Payload.Signature
JWT的三部分各自代表什么含义:
- Payload部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串
- Header和Signature是安全相关的部分,只是为了保证Token的安全性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fieIFKvq-1674715884148)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205136404.png)]
8.2.4 在Express中使用JWT
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00paV506-1674715884161)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205451783.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UO3IpItE-1674715884167)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114205614125.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQUqEjuK-1674715884173)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210004048.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRx8RRvt-1674715884181)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210323597.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ll0CwRkp-1674715884185)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114210915032.png)]
注意:执教配置成功了expressJWT中间件,就以可以把解析出来的用户信息挂载到req.use属性上
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hzTbXsC6-1674715884190)(C:\Users\popla\AppData\Roaming\Typora\typora-user-images\image-20221114211827807.png)]
以为任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
2. 向session中存储数据
express-session中间件配置成功后,就能通过req.session来访问session对象,从而存储用户的关键信息;
```js
const express=require('express')
const app=express
// 1.导入session中间件
const session=require('express-session')
// 2.配置session中间件
app.use(session({
secret:'keyboard cat', //该属性的值可以为任意字符串
resave:false, //固定写法
saveUninitialized:true //固定写法
}))
// 登录API接口
app.post('/api/login',(req,res)=>{
// 判断用户提交的登录信息是否正确
if(req.body.username !=='admin' || req.body.password !== '000000'){
return res.send({sratus: 1,msg:'登录失败'})
}
// 02:将登录成功后的信息保存到Session中
// 只有配置session中间件后req.session属性才有效
req.session.user=req.body //用户信息
req.session.islogin=true //用户登陆状态
res.send({status:0,msg:'登陆成功'})
})
// 获取用户名的接口
app.get('/api/username',(req,res)=>{
// 03: 从session中获取用户名称,响应给客户端
if(!req.session.islogin){
return res.send({status:1,msg:'fail'})
}
res.send({
status:0,
msg:'success',
username: req.session.user.username,
})
// 退出登录接口
app.post('/api/logout',(req,res)=>{
// 04:清空session信息
// req.session.destroy()函数仅清空当前用户的session信息
req.session.destroy()
res.send({status:0,msg:'退出登录成功',
})
})
// 调用app.listen方法,指定端口号,并启动服务器
app.listen(80,()=>{console.log('服务器已启动:http://127.0.0.1:80')})
})
8.2.3 JWT认证原理
[外链图片转存中…(img-pTTGZAyb-1674715884119)]
总结:用户的信息通过Token字符串的形式,保存在客户端浏览器中。服务器通过还原Token字符串的形式来认证用户的身份。
JWT的组成部分
JWT通常由三部分组成:分别是Header(头部)、Payload(有效荷载)、Signature(签名)。
三者之间使用英文的“.”分隔,格式如下:
Header.Payload.Signature
JWT的三部分各自代表什么含义:
- Payload部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串
- Header和Signature是安全相关的部分,只是为了保证Token的安全性
[外链图片转存中…(img-fieIFKvq-1674715884148)]
8.2.4 在Express中使用JWT
[外链图片转存中…(img-00paV506-1674715884161)]
[外链图片转存中…(img-UO3IpItE-1674715884167)]
[外链图片转存中…(img-fQUqEjuK-1674715884173)]
[外链图片转存中…(img-bRx8RRvt-1674715884181)]
[外链图片转存中…(img-ll0CwRkp-1674715884185)]
注意:执教配置成功了expressJWT中间件,就以可以把解析出来的用户信息挂载到req.use属性上
[外链图片转存中…(img-hzTbXsC6-1674715884190)]