1.dependencies与devDependencies
npm i xxx默认会将包安装到生产依赖下,对应package.json就是dependencies对象下。
安装包到开发环境下:devDependencies
css
1. npm i xxx --save-dev
2. npm i -D (简写)
2.常用命令
arduino
npm config list 查看配置信息
npm uninstall xxx 卸载包
npm get registry 获取配置源信息
npm config set registry 设置源信息
3.发布npm包
- 账号密码
user:dongzeping pass:wqx666666...
-
注册账号
npm adduser
-
登录账号
npm login
-
发布包(版本必须和上一次不一样)
npm publish
4. 模块化
导入
ini
const xx = require('./index')
导出
最终导出的结果看module.exports最后指向的内存单元。初始时exports和module.exports指向的是同一份内存单元
ini
exports.a = 10
module.exports = 10
例子1
b.js
ini
module.exports = 10
exports.a = 1
a.js
javascript
const a = require('./b.js')
console.log(a) //10
例子2
b.js
ini
module.exports = {
a:1
}
exports.a = 1
module.exports = {
b:1
}
exports.b = 1
a.js
css
const a = require('./b.js')
console.log(a) //{b:1}
5. 全局变量
- Buffer
- __dirname:当前文件目录的绝对路径
- __filename:当前文件的绝对路径
- process
6.path模块
6.1:basename:文件名
arduino
console.log(path.basename('/a/b/c.txt')) //c.txt
6.2:dirname:文件所在目录
css
console.log(path.basename('/a/b/c.txt')) //a/b/c
6.3:extname:文件拓展名
arduino
console.log(path.basename('/a/b/c.txt')) //.txt
6.4:join:文件路径拼接
arduino
console.log(path.join('/a','/b')) ///a/b
6.5:resolve:文件路径拼接,返回绝对路径
arduino
console.log(path.resolve(__dirname,'index.js')) //c:/a/b/index.js
6.6:parse:文件字符串解析成对象
lua
console.log(path.parse(__filename))
/*
{
root: 'C:\\',
dir: 'C:\\Users\\19394\\Desktop\\do\\not\\react测试',
base: 'node1',
ext: '',
name: 'node1'
}
*/
6.7:format:对象解析成字符串
less
console.log(path.format({
{
root: 'C:\\',
dir: 'C:\\Users\\19394\\Desktop\\do\\not\\react测试',
base: 'node1',
ext: '',
name: 'node1'
}
}))
//C:\\Users\\19394\\Desktop\\do\\not\\react测试\\node1
7. OS操作系统
1.os.cpus() cpu信息
2.os.networkInterfaces() 网络信息
3.os.platform() 系统平台
8.process进程
1. process.cwd() 当前目录
2. process.argv 输入的指令参数
-
package.json
json"dev": "node index.js --dzp --name"
-
命令行输入 npm run dev
-
index.js
arduinoconsole.log( process.argv)//可以获取--dzp --name
3. process.env 系统所有配置信息
-
下载cross-env
cssnpm i cross-env
-
package.json添加配置
json"scripts": { "dev": "cross-env NODE_ENV=dev node index.js --dzp" },
-
index.js
arduinoconsole.log(process.env.NODE_ENV)//dev
9.child_process 子进程
子进程模块可以实现多种功能,shell命令执行,脚本文件运行,进程通信等。
1. execSync(指令,[参数1,参数2])
execSync同步的执行脚本,适用于执行小脚本代码
参数1:指令字符串
参数2:数组,包含指令的额外参数
javascript
try{
let res1 = execSync('dir',['-A']);
console.log(res1.toString())
}catch(e){
console.log("error",e)
}
2. spawn(指令,[参数1,参数2])
spawn是异步的执行脚本,适用于执行一些需要长时间运行的命令。
javascript
const ls = spawn('ls');
//成功拿到脚本执行结果
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
//失败回调
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
//关闭回调
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
3. fork
fork适用于进程之间的通信。
index.js
模拟父进程发送和接收儿子进程消息
javascript
const child = fork("./child.js",[],{})
child.on('message',message=>{
console.log('收到消息',message)
})
child.send({name:"dzp"})
child.js
模拟儿子进程发送和接收父亲进程消息
javascript
process.on('message',message=>{
console.log("儿子",message)
})
setTimeout(() => {
process.send({age:25})
}, 3000);
10. util模块
util包含了许多内置工具函数
1. format
format类似于c语言的printf
lua
console.log(format("%s--%d",'dzp',123))//dzp--123
11. fs 文件模块
0. 模块导入方式
常规
文件的操作都属于异步,因此参数都需要添加回调函数。
javascript
const fs1 = require("fs")
fs1.readFile('child.js',(err,data)=>{
if(err){
console.log(err)
}else {
console.log(data.toString())
}
})
promise格式
fs/promises模块的导入让文件的所有操作都支持promise回调。
javascript
const fs2 = require("fs/promises")
fs2.readFile('child.js').then(data=>{
console.log(data.toString())
}).catch(err=>{
console.log(err)
})
1.读文件
readFile:读取小文件
javascript
fs1.readFile('index.js',(err,data)=>{
if(err){
console.log(err)
}else{
console.log(data.toString())
}
})
createReadStream:读取大文件
javascript
const stream = fs1.createReadStream("./child.js");
stream.on('data',chunk=>{
console.log(chunk.toString())
})
stream.on('end',e=>{
console.log('close')
})
2.写文件
1. 小文件:writeFile
参数1: 文件路径
参数2:文件内容
参数3:文件配置对象,可以配置文件的权限、写入方式、编码等
参数4:回调函数
javascript
//默认是覆盖写入
fs1.writeFile('./1.txt',"我是董泽萍",(e)=>{
if(e) {
console.log('error',e)
return
}
console.log('ok')
})
//追加写
fs1.writeFile('./1.txt',"我是董泽萍",{flag:'a'},(e)=>{
if(e) {
console.log('error',e)
return
}
console.log('ok')
})
2. 大文件:createWriteStream
和大文件读取一样,大文件写也是通过文件流的方式一段一段的写入
javascript
const dataList = [
"111111\n",
"222222\n",
"333333\n",
]
const writeStream = fs1.createWriteStream('./1.txt',{
flags:'a'//追加
})
dataList.forEach(item=>writeStream.write(item))
writeStream.on('close',e=>{
if(!e){
console.log('写入完成');
}
})
writeStream.end()//主动关闭
3. 追加写:appendFile
javascript
fs1.appendFile('./1.txt',"呜呜呜呜",e=>{
if(e){
return
}
console.log('ok')
})
3.删除文件
unlink
javascript
fs1.unlink('./child.js',(err)=>{
if(err) {
console.log(err)
return
}
console.log('success')
})
4.重命名文件
rename
javascript
fs1.rename('./index.html',"index2.html",e=>{
if(e){
console.log("error",e)
return
}
console.log('ok')
})
5. 创建文件夹
mkdir
javascript
const fs = require('fs');
const folderPath = path.resolve(__dirname,"./../js/1/11/111");
fs.mkdir(folderPath, { recursive: true }, (err) => {
if (err) {
console.error('Error creating folder:', err);
return;
}
console.log('Folder created successfully');
});
6. 删除文件夹
rmdir
javascript
const folderPath = 'path/to/your/folder';
fs.rmdir(folderPath, { recursive: true }, (err) => {
if (err) {
console.error('Error deleting folder:', err);
return;
}
console.log('Folder deleted successfully');
});
7. 重命名文件夹
rename
ini
const paths = path.resolve(__dirname,"../js/1")
const newPath = path.resolve(__dirname,"../js/2")
fs1.rename(paths,newPath,e=>{
if(e){
return
}
console.log('ok')
})
8. 软硬链接
硬链接
硬链接中两个文件指向同一块内存单元,二者相互关联,内容同步更新。
ini
const existingFilePath = 'path/to/existing/file.txt';
const newLinkPath = 'path/to/new/link.txt';
fs.link(existingFilePath, newLinkPath, (err) => {
if (err) {
console.error('Error creating hard link:', err);
return;
}
console.log('Hard link created successfully');
});
软链接
软链接类似电脑的快捷方式,删除一个不会影响彼此。
ini
const fs = require('fs');
const targetPath = 'path/to/target/file.txt';
const symlinkPath = 'path/to/symlink.txt';
fs.symlink(targetPath, symlinkPath, 'file', (err) => {
if (err) {
console.error('Error creating symbolic link:', err);
return;
}
console.log('Symbolic link created successfully');
});
12. markdown生成html案例
markdown文件转换成html标签
ini
const marked = require('marked');
const html = marked.parse(md文件)
案例设计
md文件更新时,网页html实时改变。
思路设计
-
监听md文件的改变
-
如果md文件修改,读取md文件借助marked工具转换成html标签,然后借助ejs模版生成html文件。
-
借助fs文件流创建生成的html文件
-
借助browser-sync工具开启服务器监听html文件,如果改变自动刷新服务器(热更新)
javascriptconst fs1 = require("fs") const ejs = require('ejs') const marked = require('marked'); const browserSync = require('browser-sync').create(); const template = fs1.readFileSync('./template.ejs','utf-8') function renderHtml() { //2.读取md文件,转换成html标签 const mdFile = fs1.readFileSync('./read.md','utf-8') const mdParse = marked.parse(mdFile.toString()) //3.ejs转换html const htmls = ejs.render(template,{content:mdParse}) //4.创建html文件 fs1.writeFile("./index3.html",htmls,e=>{ if(e){ console.log('error',e) return } console.log('生成html完成') }) } //开启服务,监听指定html文件的改变 browserSync.init({ server: { baseDir: './', // 设置服务器的根目录 index: 'index.html' // 设置默认打开的文件 }, files: ['./index.html'] // 监听的文件 }); /** * 1.监听md文件的改变 * 2.改变后渲染htmlContent-->生成html文件-->服务刷新 */ fs1.watchFile('./read.md',(cuur,prev)=>{ if(cuur.mtime!==prev.mtime) { renderHtml() } })
13. http模块
1. 创建http请求
javascript
const http = require('http')
const server = http.createServer((req,res)=>{
res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
res.end('request ok')
})
server.listen(8080,e=>{
console.log('连接成功')
})
2.get与post请求
ini
const http = require('http')
const server = http.createServer((req,res)=>{
const method = req.method
const parseUrl = url.parse(req.url);
if(method==='GET') {
//get
} else if(method === 'POST') {
//post
}
})
server.listen(8080,e=>{
console.log('链接成功')
})
3.设置响应头
参数1:状态码
参数2:响应头编码
css
res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
4.完整例子
javascript
const http = require('http')
const url = require('url')
const querystring = require('querystring')
const server = http.createServer((req,res)=>{
const method = req.method
const parseUrl = url.parse(req.url,true);//解析请求参数
const { pathname,query } = parseUrl
console.log('get请求数据',query)
if(method==='GET') {
if(pathname==='/a/b') {
res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
res.end('Hello, GET Request董泽!\n');
}
} else if(method === 'POST') {
let body = ''
req.on('data',chunk=>{
body += chunk.toString()
})
req.on('end',()=>{
const postData = querystring.parse(body)
console.log('post请求数据',postData)
res.writeHead(200, {'Content-Type': 'text/plain;charset=utf-8'});
res.end('Hello, POST Request!\n');
})
}
})
server.listen(8080,e=>{
console.log('链接成功')
})
14. 反向代理
原理
客户端访问/api请求,代理层获取请求,同时将请求发送到真正的服务端处理。 客户端不知道真实服务端地址。
用途
- 负载均衡,减轻服务端压力,提高整体分配和性能使用
- 性能优化,网页分为静态资源和动态资源,可以采用反向代理访问不同的目标地址,静态资源可以采取缓存策略提高性能等。
例子
如下示例表示访问3000服务端,而3000服务器反向代理将请求转发到了3001服务器上处理。
代理层3000代码
ini
const http = require('http');
const httpProxy = require('http-proxy');
// 创建一个反向代理服务器实例
const proxy = httpProxy.createProxyServer({});
// 监听代理服务器的请求
const server = http.createServer((req, res) => {
// 设置目标服务器的地址
const target = 'http://localhost:3001'; // 目标服务器的地址
// 执行代理转发
proxy.web(req, res, { target });
});
// 监听端口
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Reverse proxy server running at http://localhost:${PORT}/`);
});
3001服务端代码
javascript
const http = require("http")
const server = http.createServer((req,res)=>{
res.writeHead(200,{'Content-Type': 'text/plain;charset=utf-8',})
res.end('3001 响应成功')
})
server.listen(3001,()=>{
console.log('3001 open')
})
15. 前端缓存
前端网站资源分为静态资源与动态资源,通常静态资源是长时间固定不变的,而动态资源是更新的。因此系统通常将静态资源和动态资源存储在不同的服务器上,同时对静态资源添加浏览器缓存从而提高性能。
动静资源分类技术
- CDN,静态资源使用CDN缓存,而动态资源请求服务器处理
- 反向代理,静态资源和动态资源代理到不同的服务器分别处理
- 静态存储服务器,专门使用服务器存储静态资源,例如阿里云,腾讯云这种。而动态资源走服务器。
静态与动态资源分离
php
const http = require("http")
const fs = require('fs')
const mime = require('mime-types');
const path = require('path')
const urls = require('url')
const safeMime = ['text/plain','image/jpeg','audio/mpeg']
const server = http.createServer((req,res)=>{
const { method,url } = req;
const parseUrl = urls.parse(req.url,true);
const {pathname} = parseUrl
const staticUrl = path.join(__dirname,pathname)
const mimeType = mime.lookup(pathname);
/**处理静态资源 */
if(method === 'GET' && url.startsWith('/static') && safeMime.includes(mimeType)) {
try{
let fileData = fs.readFileSync(staticUrl)
res.writeHead(200,{
'Content-Type': 'text/plain;charset=utf-8',
"Cache-Control": "public, max-age=3600"
})
res.end(fileData || 'dzp')
}catch(e){
res.writeHead(200,{
'Content-Type': 'text/plain;charset=utf-8',
})
res.end('static resource error')
}
return
}
/**处理动态资源 */
if(method === 'GET' && url === '/api') {
}
})
server.listen(8080,e=>{
console.log('8080')
})
16. express
1.简单使用
javascript
const express = require('express')
const app = express()
app.get('/',(req,res)=>{
res.status(200).send('董泽萍')
})
app.listen(8080,()=>{
console.log('8080')
})
2.处理get请求:req.query
javascript
app.get('/',(req,res)=>{
const query = req.query
res.send('董泽萍')
})
3.处理post请求:req.body
javascript
app.post('/',(req,res)=>{
const query = req.body
res.send('董泽萍')
})
4. json数据注意点
前端的请求头数据如果是json数据,express必须添加json中间件处理,否则拿不到数据
javascript
const express = require('express')
const app = express()
app.use(express.json())//处理json数据
app.get('/',(req,res)=>{
res.send('董泽萍')
})
app.listen(8080,()=>{
console.log('8080')
})
5. 路由模块
express中路由按模块进行管理
index.js
javascript
const express = require('express')
const users = require('./user/index')
const app = express()
app.use(express.json())//处理前端json数据
app.use('/use',users)//处理user路由模块
app.listen(8080,()=>{
console.log('8080')
})
user/index.js
javascript
const express = require('express')
const users = express.Router()
users.get('/',(req,res)=>{
res.send('user')
})
module.exports = users;
6.中间件
express中可以自定义中间件,从而对请求头或者响应头数据进行进一步封装。
scss
const express = require('express')
const users = require('./user/index')
const app = express()
function middleWare(req,res,next) {
.....
next()//继续执行
}
app.use(express.json())//处理前端json数据
app.use('/use',users)//处理user路由模块
app.use(middleWare)//自定义中间件逻辑
app.listen(8080,()=>{
console.log('8080')
})
17. 防盗链
前端的静态资源如图片,音频,视频一般都需要添加防盗机制,从而避免其它网站窃取数据。其原理如下
前端资源请求都会在请求头携带referer字段,referer表示当前网页的地址(不是资源请求地址),因此后端根据referer判断是否是白名单网站。
scss
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['http://127.0.0.1:8080']
function checkStatic(req,res,next) {
const referer = req.get('referer')
const host = referer && new URL(referer).host
if(!referer || !whiteNetwork.includes(host)) {//无权限
res.status(404).send('not 权限')
return ;
}
next()
}
//添加防盗链中间件
app.use(checkStatic)
app.use('/statics',express.static('./static'))//设置静态资源目录
app.listen(8080,()=>{
console.log('8080')
})
18. 跨域
通过设置响应头Access-Control-Allow-Origin字段来允许跨域
1.允许全部访问
arduino
res.setHeader('Access-Control-Allow-Origin','*')
2.允许指定客户端访问
rust
res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8080')
案例
在白名单内的客户端允许跨域访问
javascript
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['127.0.0.1:8080']
function corsCheck(req,res,next) {
const origin = req.get('Origin')
if(whiteNetwork.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin',origin)
}
next()
}
app.use(corsCheck)
app.get('/',(req,res)=>{
const query = req.query//获取get数据
res.send('董泽萍')
})
app.post('/api',(req,res)=>{
const body = req.body//获取post数据
res.send('董泽萍')
})
app.listen(8080,()=>{
console.log('8080')
})
19. 请求头预检options
预检请求options在跨域请求并且满足如下任意条件就会触发
- 使用PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH非简单请求
- 自定义了请求头Content-Type为application/json,Authorization等
前端
前端使用了自定义请求头的Content-Type:application/json
less
fetch('http://127.0.0.1:8080/?name=dzp',{
headers:{
'Content-Type': 'application/json'
})
})
后端
后端通过options进行预检请求处理。
javascript
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
const whiteNetwork = ['http://127.0.0.1:5500']
function corsCheck(req,res,next) {
const origin = req.get('Origin')
console.log('origin',origin)
if(whiteNetwork.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin',origin)
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
}
next()
}
app.options("*",(req,res,next)=>{
let data = req?.query;
if(data?.name === 'dzp') {//随机测试
res.send('数据违规')
}else{
next()
}
})
app.use(corsCheck)
app.get('/',(req,res)=>{
const query = req.query//获取get数据
console.log(query)
res.send('董泽萍')
})
app.listen(8080,()=>{
console.log('8080')
})
20. SSE(后端向前端单向通信)
sse技术是客户端和服务端建立连接后,服务端向客户端单向通信的技术。
-
sse属于长连接机制,通常使用get请求
-
服务端请求头必须添加'Content-Type': 'text/event-stream'
-
发送数据必须以data开头,\n\n结尾,例如:
luares.write('data: Hello\n\n');
前端
javascript
let e = new EventSource('http://127.0.0.1:8080/sse')
e.onmessage = (data)=>{
console.log(data)
}
服务端
javascript
const express = require('express')
const app = express()
app.use(express.json())//处理前端json数据
app.get('/sse',(req,res)=>{
res.writeHead(200,{
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin':"*"
})
setInterval(()=>{
res.write(`data: ${Date.now()}\n\n`)
},2000)
})
app.listen(8080,()=>{
console.log('8080')
})
21. mysql
1. 安装
-
官网www.mysql.com/ 下载
-
配置系统环境变量,mysql.exe的目录
-
打开cmd,输入指令
cssmysql -uroot -p
-
显示welcome,表示成功。
2. vscode安装插件
- 插件搜索database,安装Database Client
- 点击左侧桶,输入数据库密码,建立连接。
3.mysql操作
1. 安装mysql工具库
css
npm i mysql2
npm i js-yaml
2. yaml文件
yaml文件用来保存系统或者数据库配置信息,其数据格式如下。每个tab是两个空格
yaml
database:
host: localhost
port: 3306
username: root
password: password
database: mydatabase
3.代码演示
javascript
const express = require('express')
const app = express()
const fs = require('fs')
const jsYaml = require('js-yaml')
const mysql = require('mysql2/promise')
let yaml = fs.readFileSync('./db.config.yaml')
let config = jsYaml.load(yaml)
let connect;
async function connect_db() {
try{
connect = await mysql.createConnection({...config.database})
}catch(e){
throw new Error('数据库连接错误',e)
}
}
connect_db()//连接数据库
app.get('/get/info',async(req,res)=>{
const query = req.query;
const {name} = query
try{
const [data] = await connect.query('select * from table1 where name = ',[name])
res.send(data)
}catch(e){
res.send('query error')
}
})
app.listen(8080,()=>{
console.log('8080')
})
24.jwt
1.安装jsonwebtoken
css
npm i jsonwebtoken
2. 首次登陆
login.html
php
fetch('http://127.0.0.1:8080/login',
{
headers:{'Content-type':'application/json'},
method:'POST',
body:JSON.stringify({name:'dzp',age:25})
})
.then(res=>res.text())
.then(data=>{
console.log(data)
})
服务端
通过jwt.sign生成token字符串,expiresIn表示过期时间
javascript
const express = require('express')
const app = express()
const cors = require('cors')
let jwt = require('jsonwebtoken');
const privateKey = 'adar11'
app.use(express.json())
app.use(cors());
app.post('/login',(req,res)=>{
const info = req.body
let token = jwt.sign(info, privateKey, { expiresIn:60*60 });
console.log('初始token',token)
res.send(token)
})
app.listen(8080,()=>{
console.log('8080')
})
3. token验证
前端进行token验证时,必须在请求头添加Authorization字段。其值必须是Bearer+' '+token
前端
javascript
const token = 'adada123213'
fetch('http://127.0.0.1:8080/info',
{
headers:{'Authorization':`Bearer ${token}`},
method:'GET',
}).then(res=>res.text())
.then(data=>{
console.log(data)
})
服务端
自定义token验证中间件,添加到需要验证的请求上。
scss
const jwtCheck = (req,res,next) => {
const token = req.headers['authorization'];
const newToken = token?.slice(7)
console.log('前端传递的token',newToken)
if (!newToken) {
return res.status(401).json({ message: 'No token provided' });
}
jwt.verify(newToken,privateKey,(err,decode)=>{
if(err) {
return res.status(401).json({ message: err });
}
next()
})
}
app.listen(8080,()=>{
console.log('8080')
})
25.文件上传
前端
通常文件上传与其它表单信息共同传递到后端处理,因此上传的数据类型是formData类型。 如下将文件和其它数据一起传递到后端
javascript
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="avatar" id="avatar">
<button type="submit">Upload</button>
</form>
const form = document.getElementById('uploadForm');
const messageDiv = document.getElementById('message');
form.addEventListener('submit', async function(event) {
event.preventDefault();
const formData = new FormData();
formData.append('name','dzp')
formData.append('avatar', document.getElementById('avatar').files[0]);
try {
const response = await fetch('http://127.0.0.1:8080/upload', {
method: 'POST',
body: formData,
});
} catch (error) {
console.error('Error:', error);
}
});
后端
-
后端通过multer处理formData的文件
-
req.file读取保存后的文件
-
req.body保存formData的非文件数据。
javascriptconst express = require('express') const app = express() const multer = require('multer'); // 设置 Multer 配置 const storage = multer.diskStorage({ destination: function (req, file, cb) { // 设置文件上传的目标目录 cb(null, 'uploads/'); }, filename: function (req, file, cb) { // 设置文件名 cb(null, Date.now()+file.originalname); } }); const upload = multer({ storage: storage }); //必须和前端的文件表单的name保持一致 app.post('/upload',upload.single('avatar'),(req,res)=>{ const data = req.body;//非文件数据 const file = req.file;//文件 res.send('ok') })
26.浏览器缓存
1.强缓存
Expires
javascript
app.get('/info',(req,res)=>{
const body = req.body
const timers = new Date(Date.now()+15*1000)//15s
res.setHeader('Expires',timers.toUTCString())
res.send('login error')
})
前端首次请求访问服务器,响应头返回Expires字段。
第二次在有效缓存时间内访问,接口直接返回200状态码,直接从缓存中获取数据。如下图size栏显示cache
Cache-Control
Cache-Control属于强缓存的第二种策略,其优先级高于expires。 Cache-Control的取值字段如下:
max-age
:浏览器资源缓存的时长(秒)。no-cache
:不走强缓存,走协商缓存。no-store
:禁止任何缓存策略。public
:资源即可以被浏览器缓存也可以被代理服务器缓存(CDN)。private
:资源只能被客户端缓存。
前端首次请求,响应头返回字段Cache-Control。
前端第二次访问,如果在缓存有效期内,接口返回状态码200,直接取缓存资源。
2.协商缓存
Last-Modified与If-Modified-Since
scss
function getFileModify(file) {
return fs.statSync(file).mtime.toISOString()
}
app.get('/info',(req,res)=>{
const body = req.body
const timer1 = req.headers['if-modified-since']
const timer2 = getFileModify('./index.js')
res.setHeader('Cache-Control', 'no-cache, max-age=2592000')//走协商缓存
res.setHeader('Last-Modified', timer2)
if(timer1 && timer1 === timer2) {
res.sendStatus(304).send()
}else{
res.sendStatus(200).send()
}
})
前端首次请求访问后端接口,响应头返回字段Last-Modified和Cache-control:no-cache表示走协商缓存了
第二次访问时,请求头自动携带If-Modified-Since字段,如果命中缓存则接口返回304状态码。
ETag与if-none-match
etag根据文件内容生成唯一hash值,文件内容改变hash值更新。etag的协商缓存优先级高于Last-Modified。
scss
function getFileHash(file) {
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
}
app.get('/info',(req,res)=>{
const body = req.body
res.setHeader('Cache-Control', 'no-cache, max-age=2592000')
const etag = getFileHash('./index.js')
const fileHash = req.headers['if-none-match']
res.setHeader('ETag',etag)
if(fileHash && etag === fileHash) {
res.sendStatus(304).send()
}else{
res.sendStatus(200).send()
}
})
前端首次访问,响应头返回字段Etag。
前端在后面访问时,请求头携带if-none-match字段去服务端进行验证,如果缓存命中,返回状态码304。
27. session登录
cookie
cookie信息在浏览器Application处查看
前端
前端模拟分为一个登录按钮和一个信息查看按钮。其中信息查看接口需要进行session权限验证
xml
<body>
<button class="login">登录</button>
<button class="info">查看信息</button>
<script>
let btnLogin = document.querySelector('.login')
let btnInfo = document.querySelector('.info')
btnLogin.addEventListener('click',()=>{
fetch('http://127.0.0.1:8080/login',{
method:"POST",
body:JSON.stringify({"name":"dzp"}),
headers:{'Content-type':'application/json'}
}).then(res=>res.text()).then(res=>{
console.log(res)
})
})
btnInfo.addEventListener('click',()=>{
fetch('http://127.0.0.1:8080/info',{
method:"GET",
}).then(res=>res.text()).then(res=>{
console.log(res)
})
})
</script>
</body>
后端
-
首次登录成功后,将用户信息保存到session
-
将sessionID通过响应头设置到cookie,传递到前端
-
前端每次接口请求自动携带cookie到服务端,之后进行权限验证。
phpconst express = require('express') const app = express() const cors = require('cors') const session = require('express-session') const cookieParser = require('cookie-parser') app.use(express.json()) app.use(cookieParser()); app.use(cors()); app.use(session({ secret: 'secret', // 用于加密会话数据的密钥,可以是任意字符串 resave: false, saveUninitialized: true })); app.use(express.static('./static')) app.post('/login',(req,res)=>{ const body = req.body if(body.name==='dzp') {//登录成功 req.session.user = body//保存session console.log(req.session.id) res.cookie('session_id', req.session.id,{httpOnly:true});//响应头设置cookie res.send('login ok') return } res.send('login error') }) app.get('/info',(req,res)=>{ const sessionId = req.cookies.session_id;//获取前端传递的cookie if(sessionId && sessionId === req.session.id) { res.send('ok') }else{ res.send('no 权限') } }) app.listen(8080,()=>{ console.log('8080') })