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') })