自我总结前端vue笔记
三阶段笔记
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){// 编码格式转换ssconsole.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 = 0socket.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);// =后面表达式的值101console.log(binding.arg);// :后面的leftconsole.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
== 条件渲染 — Vue.js
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(),就可以进入路由组件了
方法
addRoute#
添加一条新的路由记录作为现有路由的子路由。如果路由有一个
name
,并且已经有一个与之名字相同的路由,它会先删除之前的路由。
用户体验不佳时可用:
路由的命名视图 <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 名称
切换镜像资源
自我总结前端vue笔记相关推荐
- 【前端学习笔记】JavaScript + jQuery + Vue.js + Element-UI
前端学习笔记 JavaScript jQuery Vue.js Element-UI Java 后端部分的笔记:Java 后端笔记 JavaScript 基础语法(数据类型.字符串.数组.对象.Map ...
- 哔哩哔哩Allen前端vue后台管理系统笔记
哔哩哔哩Allen前端vue后台管理系统笔记 Element ui 引入 全局引入 按需引入 嵌套路由 左侧菜单栏的样式 Container布局,左侧菜单栏commonAside组件 commonAs ...
- Vue.js构建用户界面的渐进式框架(前端学习笔记1.0)
文章目录 前言 一.Vue是什么? 二.前端核心分析 1.1.概述 1.2.前端三要素 1.3.结构层(HTML) 1.4.表现层(CSS) 1.5.行为层(JavaScript) 二.前端发展史 2 ...
- Spring Boot笔记-get请求发送json数据(方便前端vue解析)
目录 基本概念 代码与实例 基本概念 这里有一个思路,后端只发送Json数据,前端vue去解析.这样的话,就可以做到前后端分离,耦合性就很低了. 代码与实例 程序运行截图如下: 得到后,使用vue去解 ...
- 前端自学Vue笔记干货(第一版,持续更新中~~~)
学习笔记 Vue笔记 nprogress使用 npm i nprogress -S 基本上都是在对axios进行二次封装.前置守卫路由或者封装成工具函数的.js文件中用到 import nprogre ...
- Vue笔记大融合总结
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" ...
- Vue笔记随笔---kalrry
Vue笔记随笔---kalrry VUE vue框架的两大核心: 一.前端开发历史 二.MV*模式 库 VS 框架 MVC架构模式 MVP架构模式 MVVM架构模式 vue是MVVM 三.开发工具 四 ...
- Vue笔记-尚硅谷-Alex
Vue笔记 Vue简介 官网 英文官网: https://vuejs.org/ 中文官网: https://cn.vuejs.org/ 介绍与描述 动态构建用户界面的渐进式 JavaScript 框架 ...
- 初学前端,学习路线图必不可少,更有【95页】初级前端模块笔记!
前言 在初学前端的时候,我们总会遇到一些问题,我们可以在网上看到很多关于前端的这些问题: 你们都是怎么学web前端的? 零基础,怎么自学好前端? 前端需要学多久,都学哪些知识? 想成为一名合格的前端工 ...
最新文章
- Linux查看CPU信息、机器型号等硬件信息
- [How TO]-ubuntu20.10上安装Pulse Secure客户端
- exit()函数详解与Exit() 和 Return() 的区别
- shell逐行读取每一列
- 全网最详系列教程-nacos配置中心详解-NameSpace、Group、DataID
- Libra教程之:运行自定义move modules
- 对于大型公司项目平台选择j2ee的几层认识
- 16 CO配置-控制-产品成本控制-产品成本计划编制-定义成本构成结构
- 有感于那个拣了两年馒头的女生~~
- 网络-UDP与TCP
- 【ArcGIS教程】(2)ArcMap中如何导入外部Excel属性数据呢?
- 凹点匹配 matlab源码,粘连类圆形目标图像的分割方法与流程
- 局域网即时通讯软件_做企业即时通讯软件,我们是认真的
- 利用Python对Excel按列值筛选并拆分表格到多个文件
- 入网许可证_入网许可证真伪鉴别
- Lumiprobe 脱氧核糖核酸丨磷酸盐 CPG 1000 固体载体
- 初试Office 365企业版E3
- Windows下Kafka集群搭建
- PHP Web项目总结
- 西门子TP900精智系列触摸屏和三菱Q系列PLC以太网通信的组态步骤
热门文章
- 流量监控软件networx使用
- abp框架学习笔记(三)--Angular和前端
- 创建一键部署的前后端(SpringBoot+Vue+nginx)项目镜像
- OpenAI发布最强的人工智能对话模型——ChatGPT,火出AI圈,给我们体验和思考
- Intellij Idea整合JProfiler插件
- 降温出门怎么穿?蕉内热皮重塑你对秋冬基本款的想象
- 数值运算pythonmopn_数的解释|数的意思|汉典“数”字的基本解释
- 2020 牛客暑期多校训练营(第一场)F
- TcaplusDB君 · 行业新闻汇编(8月11日)
- 2018年8月1日每日安全快讯 | 数十万酷视网络摄像头存在高危风险,可导致视频泄露