三阶段笔记
node基础(书写简单api接口)
-
什么是node.js
-
javascript的运行时环境
-
javascript运行在浏览器的时候:操作DOM,操作BOM,语法ECMAScript
-
javascript运行在node的时候:
-
操作文件
-
操作数据库
-
开启web服务 ...
-
不能操作DOM,不能操作BOM
-
-
-
node.js的安装
-
版本识别: 16.14.2
node -v
-
LTS: 稳定版本,建议安装 Current: 最新版本
-
主版本号.子版本号.修订版本号 v16.14.2
-
子版本号为偶数的是稳定版本,为奇数的是非稳定版本
-
修订版本号变化是是内部bug的一些处理
-
建议安装LTS的偶数版本
-
-
-
在node环境中运行js代码
-
方法一:(不推荐)
-
直接在终端运行: node+回车
-
进入js编辑环境,就可以输入js代码
-
-
方法二:
-
把你要在node环境中运行的js代码写在一个js文件
-
在终端运行: node js文件路径
-
-
node的版本管理
-
我在公司要管理多个node项目
-
项目一: node 10.16.0
-
项目二: node 14.12.2
-
就需要在电脑上安装多个不同的node版本,随时切换 就可以使用nvm这个node的版本管理工具
-
-
-
-
API
-
path模块(路径模块)
-
**path.join()**
-
将所有给定的路径拼接成一个完整的路径
-
规范化生成的路径
-
语法:path.join('/目录1','目录2','目录3/目录4','目录5')
-
返回值:/目录1/目录2/目录3/目录4/目录5
-
-
**path.resolve()**
-
同path.join()
-
区别:会将路径或者路径片段转换成绝对路径
-
path.resolve(__dirname,'static')
-
-
-
url模块
-
**url.parse()**
-
作用:把网址解析成对象
-
语法:
url.parse(urlString[,parseQueryString, slashesDenoteHost])
-
urlString:表示url地址
-
parseQueryString:布尔值,如果为true,就和解析查询字符串,否则不解析
-
slashesDenoteHost:如果为true表示//后面,/前面是主机名(如果是完整的url地址不影响)
-
返回值:url对象
-
-
**url.format()**
-
作用: 把url对象解析成url地址字符串
-
语法: url.format(url对象)
-
返回值: url地址字符串
-
-
**url.resolve()**
-
作用:把两段url片段,组成一个完整的url
-
返回值: url地址字符串
-
-
-
querystring模块(对查询字符串进行更强大的解析)
-
** querystring.stringify() **
-
作用:把对象转换成查询字符串
-
语法:querystring.stringify(要转换的对象[,自定义分隔符,自定义键值对之间的连接符])
-
默认使用&进行分隔(由第二个参数决定分隔)
-
var o1 = querystring.stringify({ name:'pipi', course:[11,22,33] },',') //name=pipi,course=11,course=22,course=33
-
-
键值用=连接(由第三个参数决定连接)
-
var o1 = querystring.stringify({ name:'pipi', course:[11,22,33] },',',':') //name:pipi,course:11,course:22,course:33
-
-
返回值:查询字符串
-
var o1 = querystring.stringify({ name:'kimi', course:['nodejs','vue','react'] }) //name=kimi&course=nodejs&course=vue&course=react
-
-
**querystring.parse()**
-
作用:把查询字符串转换成对象
-
语法:querystring.parse(要转换的查询字符串[,自定义分隔符,自定义键值
-
返回值:对象
-
var o1 = querystring.parse("name=kiki&pwd=1234") // { name: 'kiki', pwd: '1234' }
-
-
** querystring.escape()**
-
把字符串进行url编码
-
var o1 = querystring.escape("http://www.baidu.com/search?wd=千锋教育"); //http%3A%2F%2Fwww.baidu.com%2Fsearch%3Fwd%3D%E5%8D%83%E9%94%8B%E6%95%99%E8%82%B2
-
-
** querystirng.unescape()**
-
对url进行解码
-
var o1 = querystring.unescape('http://www.baidu.com/search?wd=千锋教育') //http://www.baidu.com/search?wd=千锋教育
-
-
-
fs模块
-
__filename:当前文件的绝对路径
-
__dirname: 当前文件所在目录的绝对路径
-
fs.stat(获取文件/文件夹信息)
-
fs.stat(__filename,(err,stats)=>{ // err:是错误信息 // stats:获取到的文件信息 console.log(stats) ;// 对象 // 判断是不是文件 console.log(stats.isFile()); // 判断是不是文件夹 console.log(stats.isDirectory()); })
-
-
fs.mkdir(创建文件夹)
-
fs.mkdir('./public',(err)=>{ if(!err){ // 如果没有错误信息 console.log('创建文件夹成功') } })
-
-
fs.writeFile(向文件写入内容 异步)
-
fs.writeFile(filePath,text,(err)=>{ if(!err){ console.log('文件写入成功') } })
-
-
fs.appendFile (向文件追加内容 异步)
-
fs.appendFile(filePath,text,(err)=>{ if(!err){ console.log('成功写入文件') } })
-
-
fs.readFile(读取文件)
-
//方法一 fs.readFile(filePath,(err,data)=>{ if(!err){ // 编码格式转换ss console.log(data.toString())ss } })
-
//方法二 fs.readFile(filePath,'utf-8',(err,data)=>{ if(!err){ console.log(data) } })
-
-
fs.readdir(读取文件目录)
-
const dirPath = path.resolve(__dirname,'public') // 读取文件目录 fs.readdir(dirPath,(err,files)=>{ // err:错误 // files:是一个数组,里面是读取到的文件夹里面的子文件/子文件夹名称 if(!err){ console.log(files) } }) //返回一个s
-
-
fs.rename(文件名修改)
-
fs.rename("要重命名的文件地址","新名字",(err)=>{ if(!err){ console.log('文件名修改成功') } })
-
const fs = require('fs') const path = require('path') const filePath = path.resolve(__dirname,'public','js/a.js') const newName = path.resolve(__dirname,'public','js/index.js') fs.rename(filePath,newName,(err)=>{ if(!err){ console.log('文件名修改成功') } })
-
-
fs.rm(删除文件)
-
fs.rm(filePath,(err)=>{ if(!err){ console.log('文件删除成功') } })
-
-
fs.rm(删除文件夹)
-
s.rm(dirPath,{recursive:true},(err)=>{ if(!err){ console.log('文件夹删除成功') } })
-
注:需要添加{recursive:true},表示递归删除文件夹里面的内容再删除文件夹
-
-
-
events模块
-
创建事件触发器(第一步)
-
const EventEmitter = require('events');
-
const emitter = new EventEmitter()
-
-
定义事件监听器(第二步)
-
emitter.on
(可触发多次) -
emitter.on('many',()=>{ console.log('many事件发生了') })
-
emitter.once
(只触发一次) -
emitter.once('one',()=>{ console.log('one事件发生了') })
-
触发(emit)
-
emitter.emit('many') emitter.emit('one')
-
-
-
-
stream模块(流模块)
-
fs.createReadStream(fromPath)
文件读取流(inp) -
fs.createWriteStream(toPath)
文件写入流(out) -
读取资源的相关事件
-
once
-
inp.once('data',(chunk)=>{ //chunk获取到的数据流 })
-
-
on
-
inp.on('data',(chunk)=>{ //chunk获取到的数据流 })
-
inp.on('end',()=>{ console.log('数据读取完成'); out.end() })
-
inp.on('error',()=>{ console.log('数据写入错误') })
-
-
pipe
-
inp.pipe(out) //文件读取流(inp)与 文件写入流(out)之间建立一个管道
-
-
-
-
http模块
-
http.request()
-
语法:
-
第一步: 书写请求行
-
const req = http.request(url,callback)
const req = http.request(options,callback)
-
-
第二步: 书写请求体
-
req.write()
-
-
第三步: 结束请求
-
req.end()
-
-
-
例子
-
//方法一 const https = require('https') const url = "https://www.lagou.com/" let html = ""; const req = https.request(url,(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) }) req.end()
-
方式二 const req = https.request({ host:"www.lagou.com", ports:"443", method:'get', path:'/', headers:{} },(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) }) req.end();
-
-
-
http.get()
-
语法:同http.request()
-
区别:
-
不用设置method,是get请求
-
不用书写req.write(),get请求没有请求主体
-
不用调用req.end(),会自动调用
-
-
例子:
-
https.get(url,(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) })
-
-
-
http.createServer()
-
作用:开启服务器
-
nodejs实现webServer
-
原理:根据req.url来区分客户的请求路径,根据不同的访问路径,给用户响应不同的资源
-
const http = require('http'); const fs = require('fs') const path = require('path') let num = 0; const server = http.createServer((req,res)=>{ // 只要有客户端请求,就会执行这个函数 // req:请求对象 // res:响应对象 // 获取请求路径 let url = req.url; if(url!="/favicon.ico"){ if(url=="/"){ url = "/index.html" } // 设置响应头 res.setHeader('content-type',"text/html;charset=utf-8") // 通过请求路径,拼接出文件的本地地址 const filePath = path.resolve(__dirname,'public'+url) fs.stat(filePath,(err,stats)=>{ // 文件路径不存在 if(err){ res.statusCode = 404; res.end(`${url} 不存在`) } // 如果是一个文件 else if(stats.isFile()){ res.statusCode = 200; fs.createReadStream(filePath).pipe(res) } // 如果是一个文件夹 else if(stats.isDirectory()){ fs.readdir(filePath,(err,files)=>{ res.statusCode = 200; res.end(files.join('/')); }) } }) } }) server.listen(8080,()=>{ console.log('服务器开启在:http://10.20.158.132:8080') })
-
-
特别注意: 没有http.post方法
-
-
-
cheerio(第三方模块)
-
安装:
npm install cheerio -D
-
使用方法和jquery类型
-
jquery操作的是dom节点
-
cheerio操作html字符串
-
可用于爬虫(具体看E:\2022资料\tree-grup\资料\01node-api\源代码\spider.js)
-
-
express(web 开发框架)
-
下载:
npm install express -S
-
使用三步走
-
//导入: const express = require('express'); const path = require('path') const app = express() //静态资源中间件函数: express自己写好的,你可以直接使用 //express.static(root, [options]) app.use(express.static(path.resolve(__dirname,'文件夹名'))) //监听 app.listen(9090,()=>{ console.log("http://10.20.158.121:9090") })
-
[利用 Express 托管静态文件 - Express 中文文档 | Express 中文网 (expressjs.com.cn)](https://www.expressjs.com.cn/starter/static-files.html)
-
-
-
app.use('路由',中间件函数)
-
如果不写路由,就可以匹配所有的请求
-
如果写路由,就必须以路由开头的才能匹配
-
app.use('/abc',(req,res)=>{ //只要有请求,就会执行这个函数是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法 //req:是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法 //res:响应对象,可以使用原生node的方法,也有自己的方法 res.end('all') })
-
-
路由中间件
-
如何实现?
-
一个主js文件(main.js)
-
const express = require('express'); // 这个包是为了解析请求主体(要安装) const bodyParser = require('body-parser') const app = express() app.use(bodyParser.json()) // 解析 application/json 格式的请求主体 app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体 app.use('/user',require('./routers/user.js')) app.listen(8080)
-
-
一个专门存储接口api的文件夹(routers),里面放接口文件(user.js)
-
根据需求写接口
-
// 用户相关接口 // /user/login?un=xxx&pw=xxx // /user/register?un=xxx&pw=xxx
-
-
// 路由中间件 const express = require('express'); const router = express.Router(); // 登录接口 router.get('/login',(req,res)=>{ // 获取请求的查询字符串 console.log(req.query); res.json(req.query) }) // 注册接口 router.post('/register',(req,res)=>{ // 如果要使用req.body获取请求主体 // 需要安装: npm install body-parser -S // 获取请求的请求主体 console.log(req.body) res.json(req.body) }) 注意:导出 module.exports = router;
-
-
-
app.get('路由',中间件函数)
-
匹配get请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
app.get('/login',(req,res)=>{ // express封装的res上的方法 // res.send('字符串') res.send('hello world') // res.json(对象) res.json({a:1,b:2}) })
-
响应:(选其一)
-
res.send('字符串')
-
res.json(对象)
-
-
-
app.post('路由',中间件函数)
-
匹配post请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
app.post('/api',(req,res)=>{ res.json({path:'/api',message:"post"}) })
-
-
app.all('路由',中间件函数)
-
匹配所有请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
专门用于处理404的
-
app.all('*',(req,res)=>{ res.json({code:404}) })
-
-
模板中间件(后端把html拼接好,直接返回给前端可以使用ejs模板引擎进行文件渲染)
-
下载:
npm install ejs -S
-
配置:(在当前这js文件的同级下有一个views的文件夹,里面存储list.ejs文件用来写模板中间件)
-
app.set('views',path.resolve(__dirname,'views'));
告诉app,模板文件所在的根目录 -
告诉app,模板文件用哪个包解析
app.set('view engine','ejs');
-
res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合)
-
const express = require('express'); const path = require('path') const app = express(); // 后端把html拼接好,直接返回给前端 // 可以使用ejs模板引擎进行文件渲染 // 下载: npm install ejs -S // 配置: // 告诉app,模板文件所在的根目录 app.set('views',path.resolve(__dirname,'views')); // 告诉app,模板文件用哪个包解析 app.set('view engine','ejs'); app.get('/data',(req,res)=>{ // 在配置了模板引擎以后,res就多了一个方法 : // res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合) // 在views/list.ejs中可以使用变量:age和arr和count // 把解析完的结果返回 res.render('list',{ age:"<h1>hello world</h1>", arr:[ {name:'吴波',task:'纪律委员'}, {name:'海文',task:'学习委员'}, {name:'小磊',task:'班级班长'}, ], count:100 }) }) app.listen(9090)
-
-
-
-
路由
-
运行跨域请求
-
下载: npm install cors -S
-
导入:
const cors = require('cors')
-
使用:
app.use(cors())
-
-
解析请求主体
-
npm install body-parser
-
app.use(bodyParser.json()) // 解析 application/json 格式的请求主体 app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体
-
-
-
mongodb
-
mysql:关系型数据库 -- 表结构
-
mongodb:非关系型数据库 -- 数据库里面是一个一个的json文件
-
在术语上
-
数据库: database
-
mysql => 数据库里面有表: table
-
mongodb => 数据库里面有集合: collection
-
mysql => 表里面有数据行: row
-
mongodb => 集合里面有文档: document
-
-
常用的shell命令:
-
1、帮助命令
-->help
-->db.help()
2、数据库操作命令
-->show dbs
-->use dbname 切换数据库
-->db / db.getName() 查看当前数据库名称
-->db.stats() 显示当前DB的状态
-->db.version() 查看当前DB的版本
-->db.getMongo() 查看当前DB的连接的主机地址
-->db.dropDatabase() 删除当前DB
3、创建数据库和集合
-->use project 不存在就创建,存在就切换至
-->db.createCollection('user') // 创建user集合
-->show dbs
-->show collections / db.getCollectionNames()
-->db.getCollection('music') 获取指定集合
-->db.printCollectionStats() 打印指定集合的状态
4、集合中的文档操作:
-->db.user.insertOne({}) 向集合中插入文档
-->db.user.insertMany([{},{}])
-->db.user.save({})
-->db.user.updateOne({"name":"cyr"}, {$set:{"age":100}})
-->db.user.updateMany({},{$set:{}})
-->db.user.deleteOne({"name":"jiaming"})
-->db.user.deleteMany({})
-->db.user.remove({}) // 要指出删除的条件
-->save和insert的区别:
+ 新增的数据中存在主键,则再次插入相同的主键时insert() 会提示错误
+ 而save() 则更改原来的内容为新内容
+ 没有saveMany命令
5、聚集集合查询
-->db.集合名.find({查询条件对象},{显示对象})
-->db.user.find() 查询所有记录
-->db.user.find({age:22}) 查询age=22的记录
-->db.user.find({age:{$gt: 22}}) 查询age>22的记录
-->db.user.find({age:{$lt: 22}}) 查询age<22的记录
-->db.user.find({age:{$gte: 22}}) 查询age>=22的记录
-->db.user.find({age:{$lte: 22}}) 查询age<=22的记录
-->db.user.find({age:{$gte:20, $lte:30}}) 查询age>=20 && age<=30的记录
-->db.user.find({name:/cyr/}) 查询name中包含'cyr'的记录
-->db.user.find({name:/^cyr/}) 查询name以'cyr'开头的记录
-->db.user.find({},{name:1,age:1}) 查询所有记录,只返回name和age字段(1-显示 0-不显示)
-->db.user.find({age:{$gt:20}},{name:1,age:1}) 查询age>20的记录,只返回name和age字段
-->db.user.find().sort({age:1}) 按age进行升序排列
-->db.user.find().sort({age:-1}) 按age进行降序排列
-->db.user.find({},{name:1,age:1,_id:0}).sort({age:1})
-->db.user.find({name:'cyr',age:22}) 查询name='cyr' && age=22的记录
-->db.user.find().limit(5) 只查询前5条记录
-->db.user.find().skip(10) 查询10条以后的所有数据
-->db.user.find().skip(5).limit(5) 查询第6~10条记录
-->db.user.find({$or:[{age:20},{age:25}]}) 查询age=20或者age=25的记录
-->db.user.findOne() 查询满足条件的第一条记录
-->db.user.find({age:{$gte:25}}).count() 查询满足条件的记录的总条数
-
常用:
-
use 数据库名
-
db.createCollection('user') 创建集合
-
db.user.insertOne({}) 向集合中插入文档
-
-
-
-
node操作mongodb数据库
-
使用第三方模块: mongoose
-
下载:
npm install mongoose -S
-
入门创建五步骤
-
导入
const mongoose = require('mongoose');
-
链接数据库 mongodb://localhost/数据库名
mongoose.connect('mongodb://localhost/qf');
-
创建表(第一个参数:表名尽量用复数,第二个参数:定义表里面的字段)
mongoose.model('表名',表结构
-
const Users = mongoose.model('users', { name:String, age:Number, pw:String, create_time:Number });
-
-
创建一条数据
-
const student = new Users({ name: '程磊', age:12, pw:'123456', create_time: Date.now() });
-
-
把创建好的数据插入集合中
-
student.save().then((doc)=>{ console.log('文档插入集合成功') console.log(doc) })
-
-
-
链接数据库
-
// 1 导入mongoose模块 const mongoose = require('mongoose'); // 2 链接数据库,pinxixi是要链接的数据库名称 mongoose.connect('mongodb://127.0.0.1:27017/pinxixi') // 3 获取数据库链接 const db = mongoose.connection; // 4 事件监听,判断是否链接成功 db.once('open',()=>{ console.log('数据库链接成功') }) db.on('error',(err)=>{ console.log(err) })
-
-
创建数据集合
-
require('./01'); // 导入mongoose模块 const mongoose = require('mongoose'); // 创建集合的字段名和数据类型的规范 const articleSchema = mongoose.Schema({ title:String, content:String, createTime:Number, author:String }) // 根据上面的规范创建一个articles表 const articles = mongoose.model('articles',articleSchema) // 导出创建好的articles表 module.exports = articles;
-
-
插入数据
-
//导入要操作的表 const articlesModel = require('./02') articlesModel.insertMany([ { title:'tree', content:'mongoose 进行插入操作指南3', createTime:Date.now(), author:'多多' }, { title:'four', content:'mongoose 进行插入操作指南4', createTime:Date.now(), author:'次次' } ]).then((doc)=>{ console.log(doc); })
-
-
查询数据
-
//导入要操作的表 const articlesModel = require('./03') //查询title:'tree'的数据 articlesModel.find({title:'tree'}).then((doc)=>{ console.log(doc); }) //查询时间在2022/5/11 13:23:00之前的数据 articlesModel.find({createTime:{$lt:new Date('2022/5/11 13:23:00').getTime()}}).then((doc)=>{ console.log(doc); }) //查询 id articlesModel.findById('627b46c08f5a4300fc74fe7c').then((doc)=>{ console.log(doc); })
-
-
更新数据
-
//更新一条 articlesModel.updateOne({author:"张三"},{$set:{title:"1111"}}).then(result=>{ console.log(result); // 查看更新以后的数据 articlesModel.find({}).then(doc=>{ console.log(doc) }) })
-
//更新多条 articlesModel.updateMany({author:'次次'},{$set:{title:'danger'}}).then(res=>{ console.log(res); articlesModel.find({}).then(res=>{ console.log(res); }) })
-
-
删除数据
-
//删除一条 articlesModel.deleteOne({title:'tree'}).then(res=>{ console.log(res); articlesModel.find({}).then(doc=>{ console.log(doc); }) })
-
//删除多条 articlesModel.deleteMany({title:'four'}).then(res=>{ console.log(res); articlesModel.find({}).then(doc=>{ console.log(doc); }) })
-
-
-
注意:
-
使用模块请先导入
-
如果一个请求经过多个中间件,多个中间的req和res是共享的
-
-
npm包管理工具
-
什么是npm
-
是基于commonjs规范的包管理工具
-
node安装完成以后,npm同步安装
npm -v
-
nrm可以切换仓库镜像源,建议使用淘宝镜像
-
-
管理模块
-
npm init
生成package.json文件 -
作用: 便于模块管理 便于代码转移
-
项目依赖 和 开发时依赖的区别
-
-
模块安装
-
查看指定包信息:
npm info name
-
查看安装了哪些包:
npm list
-
全局安装:
npm install name --global
-
简写: npm i name -g
-
-
本地安装项目依赖:
npm install name --save
-
简写:
npm i name -S
-
-
本地安装开发时依赖:
npm install name --save-dev
-
简写:
npm i name -D
-
-
安装指定版本
-
npm install name@版本号 --save
-
-
模块安装完成,在node_modules文件夹里面
-
这个文件夹一般不随项目转移
-
-
模块卸载
-
npm uninstall name -g
-
npm uninstall name -S
-
-
-
yarn:另一个包管理工具
-
安装yarn:
npm install yarn -g
-
yarn和npm二选一,即可
-
重要概念
-
BSR 客户端渲染(可异步渲染)
-
前端利用ajax等数据交互方式向后端请求数据
-
把数据变成html字符串,进行页面的局部刷新
-
方法:ajax/jsonp/fetch -> 获取数据 -> html节点/html字符串 -> 插入页面
-
优点:灵活,前后端分离,方便前后端更新维护
-
缺点:对SEO不友好,增加http请求次数,减缓了页面加载速度
-
-
SSR 服务端渲染
-
在后端把数据从数据库取出来以后,直接通过模板引擎渲染成html字符串
-
客户端请求页面的时候,直接返回这个页面拼接好的html字符串
-
也就是说后端可以把数据直接插入html字符串
-
优点:对SEO友好,减少http请求次数,加快了页面加载速度
-
缺点:不灵活,前后端耦合性太高
-
-
Rest风格的api设计
-
每一个url代表一种资源
-
客户端使用GET,POST,DELETE,PUT,PATCH等方式请求资源
-
从客户端到服务端的每个请求必须包含理解请求所必要的信息
-
get类型 /user 获取所有用户信息
-
post类型 /user 插入用户信息
-
delete类型 /user/1 删除id=1用户信息
-
put类型 /user/1 更新id=1用户的所有信息
-
patch类型 /user/1 更新id=1用户的部分信息
-
-
安装:
npm install -g json-server
-
创建一个db.json文件,并写入一个接收的数组
-
{ “user”:[] }
-
-
json-server --watch db.json
-
使用postman访问(http://localhost:3000/user)
-
post请求即为添加数据 参数写在body中
-
-
-
跨域问题
-
出现:协议/端口/域名有一个不同就是跨域
-
解决
-
cors跨域
-
在目标服务器设置跨域请求头
-
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,== OPTIONS"); res.header("X-Powered-By",' 3.2.1') res.header("Content-Type", "application/json;charset=utf-8");
-
-
一般不自己写,引入一个cors中间件(给目标文件引入)
-
npm install cors -S
-
const cors = require('cors')
-
app.use(cors())
-
-
-
jsonp
-
不是xhr请求,是利用script的src可以跨域的特点
-
提前准备一个函数,通过src请求后端接口返回这个函数的调用,实参就是需要的数据,函数名通过callback传递: callback=函数名
-
如果a文件需要访问b中的数据
-
a文件中需要准备一个返回的函数,并一个script标签连接请求地址
-
function getList(data){ console.log(data) } 插入一个script标签请求后端接口,后端返回一个函数调用 通过callback参数告诉后端要函数名 <script src="http://localhost:9090/list?callback=getList"></script>
-
b文件则调用
-
const express = require('express'); const app = express(); app.get('/list',(req,res)=>{ res.jsonp({host:"b server",port:9090}) }
-
-
-
代理
-
引入包
http-proxy-middleware
-
使用:
-
导入:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
-
设置
app.use('路径',createProxyMiddleware({
}))
-
app.use("/list",createProxyMiddleware({ target:'http://localhost:9090/', changeOrigin:true }))
-
-
-
-
token
-
token引入
-
客户端频繁向服务器发送请求,大量的接口需要权限验证,就需要大量的数据库查询用户名和密码是有有效,服务器压力大,有没有一种办法可以不用频繁进行数据库验证,token就是为此产生的
-
-
token长什么样?
-
LKJjLHhlHwerJlkhl324HyiyHhIUYiyGkgIUYG234kgIUYTGKgIUYiuyG
-
-
token的目的
-
减轻服务器的压力
-
减少频繁的查询数据库
-
-
token从哪里来
-
token是在服务器产生的,当客户端登录或者调用接口的时候,会返回token,客户端用户收到token以后,保存在前端(比如保存在localStorage中),之后请求其他有访问权限的后端接口时,需要把token携带上传递给后端进行验证
-
-
如何把token传递给后端呢?
-
这要根据后端的需要,通常会把token放在headers中进行传递
-
-
使用:
-
下载包:
npm install jsonwebtoken
-
[jsonwebtoken - npm (npmjs.com)](https://www.npmjs.com/package/jsonwebtoken)
-
WebSocket
-
是什么
-
用于建立服务器和客户端的双向交互通信,打开一条通道,客户端可以主动给服务器发消息,服务器也可以主动给客户端发消息,这个链接的一端就被称为socket
-
-
主要功能
-
向对方发送消息
-
基于事件驱动接收对方发送的消息
-
-
WebSocket和socket.io之间的区别
-
socket.io是一个开源库,他对WebSocket进行封装
-
增强了浏览器的兼容性
-
使用起来更方便功能更强大
-
使用on绑定事件监听
-
如果绑定的是自定义事件,用emit触
-
-
案例(E:\2022资料\tree-grup\test\04-day\socketserver)
-
聊天室开发
-
socket服务器开发(socket底层是基于net模块的)
== 接收客户端的消息(基于事件驱动的)
== 群发消息给所有客户
-
socket客户端开发
== 接收服务端的消息(基于事件驱动的),要渲染在页面上
== 把消息发送给服务器
-
解释
-
socket:就是链接到socket服务器的另一端
-
通过io可以群发
-
通过socket可以私聊
-
-
例子:
-
// const socket = io('http://10.20.158.121:9090') const socket = io('http://192.168.0.106:9090') let count = 0 socket.on('message-from-server',(message)=>{ count++ $('#top').append('<div class="row">'+message+'</div>') $('#top').scrollTop(count*35) }) $('#send').click(()=>{ let val = $('#message').val().trim() socket.emit('message-from-client',val) $('#message').val('') }) $('#message').keyup((e)=>{ if(e.keyCode == 13){ let val = $('#message').val().trim(); socket.emit('message-from-client',val) $('#message').val('') } })
-
-
-
-
pm2部署项目
-
管理多个node项目
-
安装:npm install -g pm2
-
pm2管理单个node项目
-
启动: pm2 start 启动服务器的入口js文件
-
列出所有应用: pm2 list
-
结束应用: pm2 stop id
-
删除进程: pm2 delete id
-
-
pm2管理多个node项目
-
使用命令PM2 ecosystem 或者 pm2 init,初始化配置文件ecosystem.config.js
-
配置ecosystem.config.js文件
-
name 指定项目名称
-
script 指node项目的入口启动文件
-
-
启动|重启|停止|删除多个node服务器
-
pm2 [start|restart|stop|delete] ecosystem.config.js
-
pm2 list
-
下载两个包 npm i socket.io -s nom i socket socketserver public index.html socket.io.js(nodemoudel中的) app.js
配置路由 并检测postman 写接口
-
-
vue2
-
vue: 渐进式JavaScript 框架
-
选择vue的原因
-
超快的虚拟dom,很多省心的优化
-
繁荣的生态系统
-
vue@2+vue-cli+vue-router@3+vuex@3+element ui+vue-i18n@8+...
-
vue@3+vue-cli+vue-router@4+vuex@4+element pluse+vue-i18n@9+.. == 对开发人员友好 == 兼容性: Vue 不支持 IE8 及以下版本 == 我们使用的v版本2.6.14
-
-
选项
-
data html标签中可以使用data里定义的变量,使用插值语法
{{表达式}}
,可以通过vue实例访问里面的data选项-
data:{}
-
data:function(){return {}}
-
-
el 选项指定vue实例接管的根标签,书写根标签的css的选择器
-
methods 里面定义在html标签中可以使用的方法名
-
-
$mount方法 挂载
-
template选项 指定要替换el指定的标签的html字符串
-
template的优先级高于el指定标签本身的
-
vue实例在编译html字符串的时候如果现象有template, 就用template选项里面的html字符串,如果没有就用el指定标签本身的html字符串
-
实际工作用一般都是用template,写在body里面的属性和标签名,遵循html标准,不区分大小写,写在template里面的属性和标签名,区分大小写
-
-
computed选项(计算属性)
-
在computed里定义的函数,使用的时候直接写函数名,不要写函数名(),内部会自动执行这个函数,显示返回值
-
计算属性的语法:
-
属性名: 用于获取变量值的函数
-
属性名: {get:获取变量值的函数,set:设置变量值的函数}
-
-
-
-
指令:
-
文本类指令
-
v-text
-
v-html
-
v-pre 加了这个指令的标签及其子元素不会被编译
-
v-once 加了这个指令的标签及其子元素只会被编译一次,后续就当静态内容
-
v-cloak vue编译完成以后,会把所有标签上看到的v-cloak移除(隐藏未编译的 Mustache 标签直到实例准备完毕)
-
与
[v-cloak]{ display:none; }
配用
-
-
-
v-on
-
语法
-
v-on:事件类型="事件处理函数名"
-
v-on:事件类型="事件处理函数名()"
-
v-on:事件类型="事件处理函数名(参数1,参数2,...)"
-
有一个特别的参数$event
-
-
v-on:可以简写成: @
-
-
事件修饰符
-
.prevent: 阻止事件默认行为
-
.stop: 阻止冒泡
-
.self: 只有事件的target是我自己,才触发事件,冒泡上来不触发 (大都用于父类)
-
.once:只发生一次
-
.enter
v-on:keyup.enter="addTask"
-
.right
-
v-on:keydown.alt.67=""
-
v-on:mousedown.left=""
-
-
-
v-bind
-
语法:
v-bind:属性名="js表达式"
-
简写:
:属性名="js表达式"
-
使用:
-
图片:src上
<img :src="imgSrc" alt="/>
-
class动态属性
-
动态属性原始语法
-
<div v-bind:class="className"></div>
-
-
class数组语法
-
<div v-bind:class="['bg','border']"></div>
-
-
class对象语法
-
<div :class="{bg:false,border:true,r:false}"></div>
-
-
class数组语法和对象语言一起使用
-
<div :class="['border',{bg:false},{r:true}]"></div>
-
-
-
style动态属性
-
动态属性原始语法
-
<div :style="style"></div>
-
-
style的对象语法
-
<div :style="{background:'red',border:'10px solid #ccc'}"></div'
-
-
style的数组语法
-
<div :style="[{background:'red'},{border:'10px solid #ccc'}]"></div>
-
-
-
-
-
条件渲染指令: v-if
-
通过控制节点的插入和销毁来控制显示隐藏
-
语法:
v-if="值为true就显示,值为false就隐藏"
-
如果要切换显示隐藏的有多个元素可以把他们放在一起
vue里面有一个标签标签叫做template类似js里面的()
-
-
v-show 通过控制节点的样式的display来显示和隐藏
-
语法:
v-show="值为true就显示,值为false就隐藏
-
-
v-for循环渲染
-
语法:
-
v-for="item in arr"
-
v-for="(item,index) in arr"
-
v-for="(val,key) in obj"
-
v-for="(val,key,index) in obj"
-
-
v-for和v-if强烈不推荐在同一个标签上使用
-
vue@2 : v-for 的优先级比 v-if 更高
-
vue@3 : v-if 的优先级比 v-for 更高
-
-
v-for推荐配合key属性使用
-
key必须是不能重复的
-
key建议使用字符串或者number
-
key属性是在比较不同的虚拟dom的时候起作用
-
如果key相同,就是原来的那个节点
-
如果key不相同,就不是同一个节点,就需要销毁不存在的key对应的节点
-
-
-
v-model
-
双向绑定(text变量的变化会导致界面的更新,界面上文本框或者文本域的value的变化会导致text这个变量的更新)
-
修饰符
-
.lazy
-
.trim
-
.number
-
-
复选框:
-
<input type="checkbox" value="football" v-model="flag">
-
如果flag初值是布尔值:复选框把选中的状态双向绑定到flag这个变量上
-
如果flag初值是数组:把复选框选中元素的value同步到这个数组里面
-
-
v-model用于组件元素上
-
<son v-model="text"></son>
等价于<son v-bind:value="text" @input="inputHandler"></son>
-
-
总结:
-
v-model
== 还可以使用在组件上
== 用于双向数据绑定
== 数据的变化导致界面更新
== 界面更新导致数据的更新
== v-model默认把变量绑定到value属性上
== 变量值的变化会导致元素value的变化
== v-model默认监听input事件
== 把元素的value值更新到变量上
-
-
-
-
render选项
-
优先级: render渲染函数>template模板字符串>el元素的html字符串
-
render(h){ return h('p',{ class:{ a:true, b:true }, style:{ fontSize:'100px' }, attrs:{ id:'box', index:123 } },[ h('h3',"我是p里面的h3标签") ]) }
-
-
过滤器
-
过滤器可以在
{{}}
和 v-bind:属性名="" 里面使用 -
使用方法:
-
{{变量|过滤器|过滤器(实参1,实参2,....)}}
可接多个 -
v-bind:属性名="变量|过滤器|过滤器(实参1,实参2,....)"
-
-
全局过滤器
-
语法:Vue.filter('过滤器名称',function(val){})
-
val就是使用过滤器 | 前面的变量值
-
return 返回值就是经过处理以后新值
-
-
-
局部过滤器
-
语法:
-
new Vue({ filters:{ "过滤器名称":function(val,arg1,arg2,...){ // val就是使用过滤器 | 前面的变量值 // arg1,arg2,...给过滤器函数传递的其他参数 } } })
-
-
-
-
侦听器(watch)
-
定义变量的侦听器,一旦被侦听的变量的值发生改变,就会触发侦听器
-
语法:
-
{要侦听的变量名: 数据变化的处理函数}
-
{要侦听的变量名:{
handler:数据变化的处理函数,
deep:是否深度监听,
immediate:定义函数的时候立即执行一次
}}
-
-
对象类型如何侦听?
-
当侦听的变量为一个对象(复杂数据类型)时,直接改变对象中的值,由于监听的对象,比较的是地址,地址没有变化无法监听到,需要监听地址里面内容的变化,需要深度侦听
-
如果对象list的地址变了,那这个对象的值就变化(也可以侦听到)
-
this.list = { ...this.list, name:this.newName }
-
-
-
-
自定义指令
-
指令都是v-开头的,所以我们自定义指令的时候,不用写v-, 用的时候要加上
-
全局自定义指令 - 所有vue实例都能使用
-
语法:
-
Vue.directive('指令名',{ // 当被绑定的元素插入到 DOM 中时执行inserted这个钩子函数 inserted:()=>{}, // 只调用一次,指令第一次绑定到元素时调用 bind:()=>{}, // 所在组件的 VNode 更新时调用 update:()=>{} })
-
-
简写语法:
-
Vue.directive('指令名',(el,binding)=>{ // bind和update的时候会执行 })
-
-
-
局部自定义指令 - 只有定义指令的vue实例可以使用
-
new Vue({ directives:{ '指令名': {钩子函数}, '指令名': 钩子函数简写 } })
-
例子:
-
Vue.directive('pos',(el,binding)=>{ // bind和update的时候会执行 console.log(el);// 指令所在的节点 console.log(binding);// 书写的修饰符的细节信息 console.log(binding.value);// =后面表达式的值101 console.log(binding.arg);// :后面的left console.log(binding.modifiers);// 指令的修饰符集合 // pos这个指令可以设置元素的定位 // el.style.left = '100px' el.style[binding.arg] = binding.value+"px" })
-
-
-
-
混入:mixin
-
定义一个对象,这个对象包含一些vue选项
-
全局混入:
-
Vue.mixin(要混入的对象)
-
所有实例都能使用混入的对象里面的vue选项
-
-
如果使用局部混入:
-
new Vue({mixins:[要混入的对象1,要混入的对象2,...]})
-
只有定义混入的vue实例可以使用混入的vue选项
-
-
注意:
-
混入对象的选项和实例选项有冲突,用实例的选项的
-
全局混入:慎用,一般用于做插件
-
vue@2 在mixin合并data的时候是深合并
-
vue@3 在mixin合并data的时候是浅合并
-
生命周期函数在混入的时候,不会冲突,都执行
-
-
-
中央事件总线
-
即借助vue的一个实例去提供一个’电信公司‘,其他组件可借助这个vue的实例来通信
-
实现
-
let bus = new Vue()
-
bus.$emit('其他组件自定义函数名',要传的数据)
(通过事件 发送消息的人) -
mounted(){ bus.$on('自定义函数名',(data)=>{ console.log(data) }) }
(接收信息的人,即去bus这个电信公司办卡的人)
-
-
-
-
$refs
-
使用(通常在mounted中获取)
-
如果给一个普通的标签添加 ref=’xxx‘属性,使用vm.$refs.xxx 获取其真实DOM节点
-
如果v-for循环出的标签有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个真实的dom节点的集合
-
如果一个组件有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个组件实例对象
-
-
-
provide与inject选项
-
在祖先实例中通过provide选项定义的变量,可以在其后代中通过inject选项引入来进行使用
-
使用:
-
祖先实例中:
provide:{ num:100 }
-
子孙组件中:
inject:[ ’num‘ ]
直接{{ num }}使用
-
-
-
axios
https://unpkg.com/axios/dist/axios.min.js
-
基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中
-
请求方法的简写方法
-
axios.get(url[, config])
-
axios.post(url[, data[, config]])
-
-
请求方法完整
-
axios(config)
-
aixos(url,config)
-
-
axios的实例
-
在实例中设置配置,后面直接用实例发请求,就所有的请求的baseURL都是设置好的,发请求的时候如果用server发请求,和axios发请求的方法是一样的,只不过用server发就都设置好了baseURL和responseType
-
const server = axios.create({ // axios的config怎么写,这里的配置就怎么写 baseURL:"http://localhost:8888", responseType:'json', headers:{ token:localStorage.getItem('token')||'' } })
-
-
请求响应拦截器
-
请求拦截器(给axios实例添加请求拦截器)
-
erver.interceptors.request.use(function(config){ // 请求发送成功 // config请求的配置项目,是一个对象 // 你对config做一些事情,做完以后给回去 config.headers.token = localStorage.getItem('token')||""; return config; },function(error){ // 请求发送失败 return Promise.reject(error); })
-
-
响应拦截器(给axios实例添加响应拦截器)
-
server.interceptors.response.use(function(res){ // 响应成功返回 // 在这个函数里面对返回的结果做一些事情,做完以后给回去 return res.data; },function(error){ // 响应失败 return Promise.reject(error); })
-
-
-
取消请求
-
生成取消令牌的函数
const CancelToken = axios.CancelToken;
-
生成取消令牌对象
const source = CancelToken.source();
-
在请求中添加
cancelToken:source.token
-
axios.get('http://localhost:8888/articles/list?page=1&size=10',{ cancelToken:source.token })
-
-
取消发送请求
source.cancel('不要发了')
-
-
-
组件
-
组件是一个Vue实例,有名字的,组件的概念类似自定义html标签,组件自带HTML结构,自带css样式,自带js交互,用来像html标签,可以是双标签<xxx></xxx>,可以是单标签<xxx />
-
组件的选项和vue实例的选项一样,就是data一定要是函数写法,组件中的data一般叫做组件的状态
-
全局组件 在所有vue实例里面都可以使用
-
局部组件 在定义vue实例的template选项中使用
-
插件 elementui
-
组件的生命周期
-
beforeCreate
-
created 响应式数据初始化完成以后调用,data中的数据可获取
-
beforeMount
-
mounted 会在替换完el中的节点之后调用
-
beforeUpdate
-
updated
-
beforeDestroy
-
destroyed
-
其他补充:
-
vm.$destroy()
完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
-
-
-
父子组件
-
父子组件的生命周期
-
先父组件再子组件,当父组件执行到beforemount时候,即父组件挂载之前(mounted)先挂载子组件,再挂载父组件。
-
-
父组件给子组件传值
-
在子组件上加自定义属性
自定义属性名=‘父组件属性名’
,并在子组件里添加props:[]来接收父组件传过来的属性(即接收子组件上的自定义属性名),子组件使用{{自定义属性名}}
来使用 -
props验证
-
props:['msg']
-
props:{ msg:String }
-
msg:{ type:String, required:true }
-
带有默认值的对象
-
props:{ msg: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default:function(){ return {a:1,b:2} } }, }
-
自定义验证函数,返回值true表示通过验证
-
props:{ msg:{ validator:function(val){ // val就是给msg注入的具体的值 return true; } } }
-
-
-
-
-
子组件将数据给父组件
-
使用
vm.$emit('要触发的事件类型','给事件处理函数的实参1','给事件处理函数的实参2',...)
-
在子组件上定义一个自定义事件,
@自定义事件名f=‘自定义函数名’
,当子组件中的一个事件触发时(点击子组件按钮触发事件a),在事件a中使用this.$emit('自定义事件名f',子组件要传递的参数(多个参数用,分隔))
,父组件调用自定义函数进行接收子组件传来的值
-
-
sync修饰符(父子组件传值的语法糖)
-
<son :msg="title" @update:msg="msgHandel"></son>
-
上述可改写为:
<son :msg.sync="title"></son>
-
-
-
异步组件
-
Vue.component('child2',function(resolve,reject){ setTimeout(()=>{ resolve({ template:`<h1>hello child2</h1>` }) },3000) })
-
-
动态组件
-
语法: <component is='组件名称'></component>
-
通常与
keep-alive
一起用 -
keep-alive
包裹动态组件,会"缓存"不活动的组件实例,而不是销毁他们 -
<keep-alive> <component :is="which"></component> </keep-alive>
-
-
把回调延迟到下次dom更新完成执行
+ Vue.nextTick()
+ vm.$nextTick()
clickHandler(){ this.msg = "哭唧唧"; this.$nextTick(function(){ console.log(document.querySelector('h1').innerHTML) }) }
-
-
插槽v-slot指令
-
组件的outerHTML一般会被丢弃, 除非组件里面有可扩展的插槽,组件通过<slot>标签可以开辟一个插槽
-
<slot></slot> 是匿名插槽,名字是default,所有内容都可以放到这个插槽里面
-
<slot name='xxxxx'></slot> 是具名插槽,只有指定这个插槽名才能插入
-
指定插槽名字插入内容的语法
<template v-slot:插槽名字>要插入插槽的内容</template>
-
给插槽里面的内容传值
-
组件里面定义的插槽 :<slot a='xxx' b='xxx' c='xx' name='插槽名'></slot>
-
使用:
v-slot:插槽名 = "变量名"
等价:var 变量名 = {a:'xxx',b:'xxx',c:'xx'}
-
v-slot: 可以简写成 #
-
-
-
插件机制
-
定义插件:
export default {install:function(Vue,options){}}
-
例子:
-
export default { install:function(Vue,options){ console.log(Vue); console.log(options) console.log("Vue.use的时候其实是调用obj的install方法,并传入第一个实参是Vue") } }
-
-
使用插件:
-
import MyPlugin from 'xxx.js'
-
Vue.use(MyPlugin)
-
例子
-
<script type="module"> //导入 import myPlugin from "./plugin.js"; //注册插件 Vue.use(myPlugin,{banji:'sz2202',number:66}) <script/>
-
//导入的是通过export导出的变量 import {aButton,bButton} from "./myElement.js //注册需要的全局组件 Vue.component('a-button',aButton)
-
-
-
-
过渡
-
在插入、更新或者移除 DOM 时,只要把要动画的元素放到transition标签就就会添加动效果,过渡的名字默认是: v
-
开始插入: v-enter
-
插入结束: v-enter-to
-
插入过程: v-enter-active
-
开始移除: v-leave
-
移除结束: v-leave-to
-
移除过程: v-leave-active
-
例子(常用)
方案一: .v-enter,.v-leave-to{ transform: translateX(200px); opacity: 0; } .v-enter-to,.v-leave{ transform: translateX(0); opacity: 1; } .v-enter-active,.v-leave-active{ transition:all 1s; }
-
方案二: .fade-enter-active{ animation: move 1s; } .fade-leave-active{ animation: move 1s reverse; } @keyframes move { from{ transform: translateY(200px); opacity: 0; } to{ transform: translateY(0); opacity: 1; } }
-
-
通过给transiton设置name可以修改名字 name='xxx',原来类名中的 v- 就变成 xxx-
-
元素之间的过渡可以通过mode设置元素进入和移除的顺序
-
mode='out-in'
先出后进 -
mode='in-out'
先进后出
-
-
多元素过渡
-
可以通过mode设置进入和移除的顺序
-
需要给每个过渡的元素一个唯一的key
-
transition里面只能一个节点
-
-
初始过渡
-
给transition添加appear属性,初始就有过渡效果
-
-
如果过渡的元素有多个,那么要写在标签 transition-group中
-
vue的特点
-
数据的改变会导致界面的更新
-
-
其他:
-
el和$mount区别:
-
都是一个作用: 让vue实例接管页面上的标签
-
el的优先级高于$mount,vue实例看有没有el选项,如果有就不调用$mount,只有没有el选项,才调用$mount
-
-
-
扩展:
-
扩展:vue3版本中没有$mount方法了
-
contentmenu
-
better-scroll插件
https://better-scroll.github.io/docs/zh-CN/guide/#%E8%B5%B7%E6%AD%A5
-
面试题:
-
methods 和 computed有什么区别?
-
methods没有缓存,只要数据改变就会调用
-
computed有缓存,只有参与当前计算属性的data里面的变量变化的时候,才会调用,否则直接使用上次缓存的值
-
-
v-if vs v-show
-
-
methods和computed的定义函数不要写箭头函数
-
vuecli
-
如何使用:
-
下载:
npm install -g @vue/cli
-
创建项目:
vue create hello-world
-
-
router
-
如何配置路由
-
参考 :
https://router.vuejs.org/zh/guide/#javascript
-
-
路由跳转的方法
-
this.$router.go(-1);
-
this.$router.push('配置的路由地址')
-
-
动态路由
-
例子
-
http://localhost:8080/detail/:id/:num
-
http://localhost:8080/detail/1/200
-
-
配置:
path:'detail/:id',
-
访问:
xxx/detail/xx
-
获取:可在created的钩子中 使用 $route获取
-
console.log(this.$route.params.id)
-
匹配以后,里面的 this.$route.params = {id:1,num:200}
查询字符串
-
http://localhost:8080/login?a=1&b=2
-
a=1&b=2
就是查询字符串 -
当匹配路由组件以后,里面的:
this.$route.query = {a:1,b:2}
-
-
-
嵌套路由
const routes = [ { path:'/user', component:Layout, children:[ { //path:"/user/manage", 或者 path:'manage', component:UserManage }] }]
-
-
Vue Router
-
路由守卫
-
router.beforeEach((to,from,next)=>{})
-
to: 要进入的的路由对象
-
from: 从哪里来的那个路由对象
-
调用next(),就可以进入路由组件了
-
-
-
方法
-
用户体验不佳时可用:
-
路由的命名视图 <router-view name='aaa'></router-view> 在路由添加 compons:{ 'aaa':()=>import() } props:{ aaa:true }
-
-
动态路由如何接收参数(如id)
-
方法一:
path:'detail/:id',
-
接收:
this.$route.params.id
-
-
方法二: 如果设置props:true,那当前动态路由参数会作为props传入匹配的组件,匹配的组件需要设置同名的props来接收,就可以直接使用
-
path:'detail/:id',
'detail/:num' -
props:true
-
组件中接收
props:['num']
-
-
-
-
multi-page 模式下(多页面模式)
-
需要给vue.config.js配置相关信息
-
参考
[配置参考 | Vue CLI (vuejs.org)](https://cli.vuejs.org/zh/config/#pages)
-
案例:
-
pages:{ index:"src/index/main.js", sz:{ // page 的入口 entry: 'src/sz/main.js', // 模板来源 template: 'public/sz.html', // 在 dist/index.html 的输出 filename: 'sz.html', // 当使用 title 选项时, // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title> title: 'SSSZZZ', // 在这个页面中包含的块,默认情况下会包含 // 提取出来的通用 chunk 和 vendor chunk。 chunks: ['chunk-vendors', 'chunk-common', 'sz'] } },
-
-
-
-
如何设置代理:
-
参考文档:
[配置参考 | Vue CLI (vuejs.org)](https://cli.vuejs.org/zh/config/#devserver-proxy)
-
语法:
-
方法一:
proxy:'跨域地址'
-
方法二:
proxy:{ '/users':target:'跨域地址',changeOrigin:true,pathRewrite:{'/users':''} }
-
-
案例:
-
devServer:{ // 所有的请求都代理到一个服务器 // proxy:"http://10.20.158.132:9090" // 所有的请求不是代理到一个服务器 proxy:{ // 如果请求自己服务器的地址有含有/api90 "/api90":{ // 代理到的目标服务器 target:"http://10.20.158.132:9090", // 是否修改主机名 changeOrigin:true, // 是否要修改请求路径 pathRewrite:{ /访问地址是否包含用以区分的地址'/api90' "^/api90":"" // 原始请求地址如果是:http://localhost:8080/api90/user/login // 代理以后变成:http://10.20.158.132:9090/user/login } }, "/api":{ target:"https://mm.cxbiao.cn", changeOrigin:true // 原始请求地址如果是:http://localhost:8080/api/search?wd=xxx // 代理以后变成:https://mm.cxbiao.cn/api/search?wd=xxx } } }
-
-
切换生产环境与上线环境的地址
-
publicPath:process.env.NODE_ENV==='production'?'/muti/':'/',
-
-
-
关于项目打包的问题(
npm run build
)-
打包之后如何处理缓存(因为请求地址一致,导致页面不更新)
-
实验:
-
打包之后删除,再修改一些代码,再次打包
-
代码的hash:代码发生变化,js中的文件名发生改变 只要项目的内容发生改变,再次打包代码的hash会发生改变 两种:
-
chunk hash:只和当前代码块有关
-
hash:从整个项目代码生成hash(一般用这个)
-
-
-
多个组件都需要使用token,如何解决?
-
方法一:存储在本地存储(不推荐) token发生变化时候,页面上使用的token不能同步更新token
-
方法二:使用原型(不可取) main.js :this.$proptype.token = '123' 使用:token:this.token
-
方法三:(vuex 状态管理工具) 创建了一个全局响应变量 vue2 => vuex@3 src/store/index.js 导入 => 注册 => 使用 在main.js注入 数据更新导致页面的更新 修改:
-
方法一:直接修改devtool不会记录 this.$store.state.token = token(不可用)
-
方法二:修改state里面的值只能调用mutations中的方法,会被devtool记录
-
-
-
vue的ssr(vue实例的服务端渲染)
-
如何使用ssr
-
第一步:创建一个vue实例
-
const app = new Vue({ template:` <h1>hello {{name}},i am {{age}} years old!</h1> `, data(){ return{ name:'JC-T', age:12 } } })
-
-
第二步:创建一个renderer(需下载一个包)
npm i vue-server-renderer -S
-
const renderer = require('vue-server-renderer').createRenderer()
-
-
第三步: 将vue实例编译成html字符串
-
renderer.renderToString(app,(err,html)=>{ if(err) throw(err); console.log(html) })
-
-
参考文档: http://nuxtjs.cn(做vue的服务器渲染)
-
-
辅助函数
-
导入:
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
-
mapState
-
对应:state
-
访问:
this.$store.state.XXX
-
-
放在计算属性中(computed)
-
...mapState(['要使用的state内的响应数据假设为token'])
-
-
访问:
this.要使用的state内的响应数据(token)
-
模块怎么访问:
...mapState('模块名',['name(模块理state内的数据)'])
-
-
mapGetters
-
对应:
getters
-
访问:
this.$store.getters.XXX
-
-
放在计算属性中(computed)
-
...mapGetters(['要使用的getters内的数据假设为count'])
-
-
访问:
this.要使用的getters内的数据假设为count
-
模块怎么访问:
...mapGetters('模块名',['count(模块理getters内的数据)'])
-
-
mapMutations
-
对应:
mutations
-
使用:
this.$store.commit('mutations内的方法名',payload(要传递的参数),)
-
-
放在方法中使用(methods)
-
...mapMutations(['mutations内的方法名'])
-
-
模块怎么访问:
...mapMutations('模块名',['SET_NAME'])
-
-
mapActions
-
对应:
actions
-
使用:
this.$store.commit('mutations内的方法名',payload(要传递的参数),)
-
-
放在方法中使用(methods)
-
...mapMutations(['mutations内的方法名'])
-
-
模块怎么访问:
-
...mapMutations('demo',['SET_NAME'])
-
可以重命名,把数组写成对象
-
...mapMutations('demo',{a:SET_NAME})
-
-
-
用的时候就是: this.a
-
-
-
vuex
-
state: 所有组件都可以使用的状态
-
mutations:定义修改state里面的值的方法
-
mutations:{ // mutations里面的方法不能有异步 'SET_TOKEN':function(state,payload){ // state就是当前的 store.state // payload就是你调用方法传入的参数 state.token = payload.token } }
-
调用mutations里面的方法
-
语法1:
store.commit('mutations里面的方法名','参数')
-
语法2:
store.commit({type:'mutations里面的方法名','参数'})
-
-
-
-
vue2响应的底层原理
-
数据的改变会导致界面的更新,需要能监听到数据的变化,从而在变化的时候执行一次界面的更新
-
**Object.defineProperty**
能监听对象的属性的操作-
获取属性值
-
设置属性值
-
Object.defineProperty(obj,attrName,{})
-
第一个参数: 要监听的对象
-
第二个参数: 要监听的对象的属性名
-
第三个参数: 是一个对象,定义赋值和读取的时候要执行的方法
-
set:function(){} get:function(){}
-
-
-
-
mvvm的模型
-
model:数据模型
-
view: 视图
-
vm: vue实例
-
-
发布订阅模式
-
-
vue3的响应式原理
-
Proxy对象
-
用于创建一个对象的代理
-
实现基本操作的拦截和自定义(如属性的获取和赋值)
-
语法:
const p = new Proxy(要代理的目标对象,{})
-
第二个参数为一个对象
-
{ set:function(target,prop,val){ // 当通过代理设置对象的属性值的时候,会触发 // target:原始对象 // prop:你通过代理要操作的那个属性名 // val:你通过代理要设置的属性值 console.log(target,prop,val) // 把属性值设置给原始对象 target[prop] = val; } get:function(target,prop){ // 当通过代理获取对象的属性值的时候,会触发 // target:原始对象 // prop:你通过代理要获取的那个属性的名称 console.log(target,prop) return target[prop] } }
-
-
-
-
vue3
-
vite(vue3使用的框架)
-
内部使用rollup打包你的代码
-
创建项目:
npm init vite projectName -- --template vue
-
开启服务:npm run dev
-
打包压缩文件: npm run build
-
常用配置:
vite.config.js
-
配置@别名
-
const path = require('path') { resolve:{ alias:{ '@':path.resolve(__dirname,'src') } } }
-
-
.vue扩展名不能省略
-
-
回顾:
-
vue-cli
-
内部使用webpack打包你的代码
-
安装:npm install -g @vue/cli
-
创建项目:
vue create projectName
-
开启服务:
npm run serve
-
打包压缩文件:
npm run build
-
常用配置: proxy, alias(@ => src)
-
-
-
一些语法的小变化
-
template里面可以有多个根节点
-
挂载不会替换原来的#app
-
自定义事件要预先说明
-
v-bind:abc.sync='xxx' => v-model:abc="xxx"
-
this.$emit('update:abc')
-
单文件组件的setup语法糖(单文件组件的组合式API的语法糖 setup里面this不是组件实例 定义在setup里面的顶层的变量,函数,组件都可以直接使用)
-
顶层可直接 使用awite(异步语法)
-
顶层的变量,函数,组件可以使用使用
-
定义响应式变量: ref(xx) reactive(xxx)
-
定义计算属性: computed(()=>{})
-
定义侦听器:
-
watch(响应变量,()=>{})
-
watchEffect(()=>{})
-
-
定义生命周期:
-
onMounted(()=>{})
-
没有onBeforeCreate和onCreated
-
-
定义自定义事件
-
const emits = defineEmits(['自定义事件名称1','自定义事件名称2'])
-
vue3
-
const emits = defineEmits(['自定义事件名'])
-
emits('自定义事件名',参数)
-
-
回顾vue2
-
this.$emit('事件名',参数)
-
-
-
定义props
-
1 获取外部传入的props:
const props = defineProps(['接收的属性1','接收的属性2'])
-
-
-
vue3的组件生命周期
-
beforeCreate created
-
beforeMount mounted
-
beforeUpdate updated
-
beforeUnmount unmounted
-
-
-
响应式变量:
-
使用前要导入
import { ref,reactive }
-
ref (针对非对象类型)
-
let title = ref("");
-
-
reactive (针对对象类型)
-
let person = reactive({name:"lucy",age:12})
-
-
-
组合式API
-
在script组件上加上
setup
-
是单文件组件的组合式API的语法糖
-
setup里面this不是组件实例
-
定义在setup里面的顶层的变量,函数,组件都可以直接使用
-
-
定义计算属性
-
导入
import { computed }
from ‘vue’ -
定义:
-
const count = computed(()=>{ return xxxx })
-
-
-
定义监听器
-
导入
import { watch }
from ‘vue’ -
定义:
-
watch(监听的变量,()=>{ }) `{要侦听的变量名:{` ` handler:数据变化的处理函数,` `deep:是否深度监听,` `immediate:定义函数的时候立即执行一次` ` }}`
-
-
定义(watchEffect会立即执行一遍,相当于有immediate功能)
-
watchEffect(()=>{})
-
-
-
-
vue-Router(vue3)
-
下载: npm i vue-router -S
-
在文件夹
/src/router/index.js
内写路由-
第一:导入:
import {createRouter,createWebHashHistory} from 'vue-router' }
-
第二:定义路由地址
-
const routes = [ { path:'/login/:id', component:()=>import('../xxxx') } ]
-
-
配置
-
const router = createRouter({ history:createWebHashHistory(), routes })
-
-
导出:
export default router
-
-
在main.js中导入,挂载
-
import router from "./router" createApp(App) .use(router) .mount('#app')
-
-
在组件中使用:
-
导入:
import {useRouter,useRoute} from 'vue-router'
-
使用:
-
const router = useRouter()
//可以通过useRouter来获取router对象 -
在函数在调用跳转方法
router.push('/dashboard')
-
const route = useRoute()
//可以通过useRoute来获取route对象 -
在模板中使用获取动态路由的id
{{route.params.id}}
-
匹配所有或404
-
path:"/:pathMatch(.*)*", component:()=>import('../views/404.vue')
-
-
-
-
-
vuex(vue3)
-
使用了vite框架
-
下载
npm i vuex -S
-
在文件夹
/src/store/index.js
内写状态管理-
第一:导入:
import {createStore} from 'vuex'
-
第二:定义状态管理
-
const store = createStore({ state:{ num:10 }, mutations:{ set_num:function(state,payload){ state.num = payload } } })
-
导出:
export default store
-
在main.js中导入,挂载
-
import store from './store' createApp(App) .use(store) .mount('#app')
-
-
在组件中如何使用
-
导入
import {useStore} from 'vuex'
-
使用:
-
const store = useStore()
-
{{store.state.xxx}}
-
-
-
-
-
-
其他
-
nrm test
查看镜像资源 -
nrm use 名称
切换镜像资源
-
-