Vue3 + koa2 + mysql 开发中的杂谈
Vue3 + koa2 + mysql 开发中的杂谈
一、Vue3开发
1、v-md-editorMarkdown文本编辑插件
理由:本插件在使用过程中完美兼容vue3,且功能较丰富,有详细完整的教程
vue3使用一定要安装对应的版本
2、vue3中组件通信
1、祖孙通信使用依赖注入provide/inject,,兄弟使用mitt库
依赖注入在main.js中使用
// 依赖注入的方式,provide不能对象的方式写
// app.provide({
// 'axios': axios,
// BasicUrl: 'http://localhost:5030/'
// })
app.provide('axios', axios)
app.provide('BasicUrl', BasicUrl.Url)在vue组件中可正常使用
const axios = inject('axios')provide: {name:value
}
2、父子使用props
provide/inject是祖孙间通信,父子间通信最好用props
父组件
子组件接收
props两种接收方式
1、props:["blogdetail"]
2、props:{blogdetail:{type:String/Objectdefault:'' //没有输入时的默认值}
}
3、兄弟使用mitt库
vue2.0时经常用事件总线 $emit 和 $on进行组件间的通信,vue3.0中这两个方法被移除了,无法使用Vue.prototype.$bus = new Vue()
vue兄弟组件通信可以引用第三方库mitt库 !!!也是官方推荐的想在Vue3当中使用事件总线当然也非常简单的啦一共分为三个步骤
1.安装
npm install --save mitt2.挂载
//main.js中
import mitt from "mitt"
import { createApp } from "vue"
const app = createApp()//正常配置
//挂载事务总线
const vueEvent = new mitt()
app.config.globalProperties.vueEvent = new mitt()3.使用
import {getCurrentInstance} from 'vue'const vueEvent = getCurrentInstance()?.appContext.config.globalProperties.vueEvent
//发送
vueEvent.emit('handleCurrentNo', 1)
//接收onMounted(() => {vueEvent.on('handleCurrentNo', (val: number) => {state.currentNo = val})})onUnmounted(() => {vueEvent.off('handleCurrentNo')})
3、axios配置及操作
//axios.js
import axios from "axios";
import { ElMessage } from "element-plus";let http = axios.create({baseURL:'http://localhost:5030/'
})//请求拦截
http.interceptors.request.use( config => {if(localStorage.elementToken){config.headers.Authorization = localStorage.elementToken}return config
})//响应拦截
http.interceptors.response.use( res => {return res
}, err => {console.log(err.response);ElMessage.error(err.response.data.msg)
})export default http//在main.js中直接使用provide传出去
import axios from './axios.js'
app.provide('axios', axios)//然后在组件中直接引用即可
const axios = inject('axios')//由于axios配置时设置了拦截,所以例如element自带的文件上传需要在头上加上token令牌// let config = {// headers: {// Authorization: localStorage.elementToken,// },// };//自定义的上传文件,例如图片时,在拿到文件后,需要将其实例化//实例表单// let formdata = new FormData();//添加表单// formdata.append("file", files[0]);然后直接使用axios上传formdata即可,后端接收文件是const files = ctx.request.files.file
axios.post('访问的url',上传的值,config(配置的头文件,一般没有)).then((res) => {console.log(res.data);}).catch((err) => {console.log(err);});
或者
axios({url:'',method:'post',data:'上传的值'header:{"Content-Type": 'application/x-www-form-urlencoded'} //配置
})//get请求
//单纯的接收 XXX/user/test接口传回的值
vue端:axios.get('/user/test')
koa端:user.get('/test',async(ctx) => { })//带参获取值
vue端:axios.get('/user/test/' + testvalue)
koa端:user.get('/test/:name',async(ctx) => { const testvalue = ctx.params.name })//post请求
vue端:axios.post('/user/test',testvalue) //此处的testvalue应为对象
koa端:user.get('/test',async(ctx) => { const testvalue = ctx.requestbody.body.testvalue或键值对的名字一样时const {testvalue} = ctx.requestbody.body})
4、常用函数
//生命周期,页面初始时即加载的服务
onMounted(() => { });//计算属性
const double = computed(()=>{ })//需要在 DOM 更新之后再执行一段代码时,可以借助nextTick实现
nextTick(() => {});
5、注意事项
//使用element表单时的注意
立即创建 重置
//一组表格中选取一行数据修改思路
1、获取到后台的对象的数组
2、向数组中的每一个对象添加一个boolean类型的键值对
为数组中每一个对象添加{edit:false}键值对
let addedit = res.data。arr;addedit.forEach((element) => {element["edit"] = false;});
3、通过v-if或v-show实现修改功能,注:v-if和v-show前不需要':'绑定,否则功能无法实现//将后端获取的数据存储到本地
localStorage和sessionStorage一样都是用来存储客户端临时信息的对象
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
用法:
setItem (key, value) —— 保存数据,以键值对的方式储存信息。
getItem (key) —— 获取数据,将键值传入,即可获取到对应的value值。
removeItem (key) —— 删除单个数据,根据键值移除对应的信息。
clear () —— 删除所有的数据
key (index) —— 获取某个索引的key
例如:token为已获取到的值
localStorage.set("elementToken",token)//文件下载
const downloadU = BasicUrl + "/files/download/" + row.filename;window.open(downloadU, "myIframe");
通过该指令可直接下载文件,后端会接受到该路径,接收token验证,所以需要需要将/files/download/*加入到koa-jwt白名单中, /^\/files\/download\/*/const send = require('koa-send');
//实现下载功能
fileList.get('/download/:name', async (ctx) => {const name = ctx.params.nameconsole.log('filename', name);const path = `public/uploads/${name}`;console.log('fileQueryPath', path);ctx.attachment(path);await send(ctx, path);
})
二、Koa2开发
1、调用其三方接口,请求其他数据
参考node请求
//wallhaven.cc/api的图片接口非常友好,有相应的api文档,登陆账号后就可获得密钥
const Router = require('koa-router');
const queryback = require('../util/db/db.js')
const otherapi = new Router();const request = require('request')
otherapi.get('/wallhaven/:name', async (ctx, next) => {let pageid = ctx.params.name//request需要异步执行,let dd = await new Promise((resolve, reject) => {//node封装的请求中间件GET请求request({url: 'https://wallhaven.cc/api/v1/search',method: 'GET',//请求带参数时,get中params中填写的数据对应的为qs中的对象,结果为json//post中body填写的数据对应的为form的对象,具体看请求头qs:{'sorting':'toplist','topRange':'1d','purity':'100','page':pageid,},headers: {"Content-Type": 'application/json',}},function (error, response, body) {if (!error && response.statusCode == 200) {resolve(JSON.parse(body))} else {console.log('失败', error);}})})if (dd.data !== null) {ctx.response.status = 200ctx.body = {code: 0,msg: 'success',imgs: dd.data}} else {ctx.response.status = 404ctx.body = {code: -1,}}
})
2、async、await、Promise之间的联系
async 函数
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定
await 表达式
- await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
- 如果表达式是 promise 对象, await 返回的是 promise 成功的值
- 如果表达式是其它值, 直接将此值作为 await 的返回值
注意
- await 必须写在 async 函数中, 但 async 函数中可以没有 await
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
//异步操作一般使用,该命令运行在async中
//let dd = await new Promise((resolve, reject) => { }<script>
function fn1() {return Promise.resolve(1) }function fn2() {return 2 }function fn3() {return Promise.reject(3)// return fn3.test() // 程序运行会抛出异常}function fn4() {return fn3.test() // 程序运行会抛出异常}// 没有使用 await 的 async 函数
async function fn5() {return 4 }async function fn() {// await 右侧是一个成功的 promiseconst result = await fn1()// await 右侧是一个非 promise 的数据// const result = await fn2()// await 右侧是一个失败的 promise// const result = await fn3()// await 右侧抛出异常// const result = await fn4()console.log('result: ', result)return result+10}async function test() {try {const result2 = await fn()console.log('result2', result2) } catch (error) {console.log('error', error) }const result3 = await fn4()console.log('result4', result3) }// test()
</script>
3、koa-jwt的白名单
const {PRIVATE_KEY
} = require('./util/encrpytion/md5.js')
const koaJWT = require('koa-jwt')//使用kaoJwt拦截token
app.use(koaJWT({// 解密 密钥secret: PRIVATE_KEY,algorithms: ["HS256"]}).unless({path: ['/home','/login','/login/register','/email','/email/examEmail','/email/forget',/^\/files\/download\/*/,] //此处使用正则表达式,此处是下载的命令//⽩名单,除了这⾥写的地址,其 他的URL都需要验证}));白名单中需要使用正则表达式可放行/files/download/:name
4、解析token令牌,获取用户名
//设置的加密,解码
md5.js
const crypto = require('crypto')function md5(s) {// 给密码加密//注意参数需要为string类型,否则会报错return crypto.createHash('md5').update(String(s)).digest('hex');
}module.exports = {md5,// 处理密码的密钥PWD_SALT: 'xd_node',// 处理token的密钥PRIVATE_KEY: 'xd_blog',// token的过期时间EXPIRESD: 1000 * 60 * 60 * 24
}//解析令牌
selectUser.js
const {PRIVATE_KEY,
} = require('../encrpytion/md5.js')
const jwt = require('jsonwebtoken')const selectUser = async (ctx, next) => {let token = ctx.request.header.authorization.split(" ")[1];if (token) {let jiemi = await jwt.verify(token, PRIVATE_KEY, (err, data) => {return data})let myusername = jiemi.usernamereturn myusername} else {return false}
}module.exports = {selectUser
}在接口中使用,导入即可
const {selectUser
} = require('../util/file/selectUser.js')const myusername = await selectUser(ctx)
5、关于跨域问题
在前后端交互的过程中难免会遇到跨域的问题,解决的方式有如下
1、在前端解决跨域问题
2、在后端解决跨域问题
3、通过nginx解决问题
由于各种各样的问题且知识有限的问题,在解决跨域问题是优先考虑后端解决跨域问题,即使用cors解决
在koa2中直接使用koa2-cors即可
在app.js中导入
const cors = require(‘koa2-cors’)
app.use(cors())
6、app.js文件解析
//整个函数的入口
const Koa = require("koa2"); //构造函数
const app = new Koa(); //声明一个实例
const port = 5030; //端口号
const router = require('./router/index.js');
const koaStatic = require('koa-static')
const chance = require('chance')const {PRIVATE_KEY
} = require('./util/encrpytion/md5.js')
const koaBody = require('koa-body')
const koaJWT = require('koa-jwt')const cors = require('koa2-cors')
app.use(cors())const path = require("path")//koa-static可将文件路径转换为http路径,文件起始地址需先转到根路径,然后调用根目录下的静态目录下文件目录
app.use(koaStatic(path.join(__dirname, 'public')))//使用kaoJwt拦截token
app.use(koaJWT({// 解密 密钥secret: PRIVATE_KEY,algorithms: ["HS256"]}).unless({path: ['/home','/login','/login/register','/email','/email/examEmail','/email/forget',/^\/files\/download\/*/,] //此处使用正则表达式,此处是下载的命令//⽩名单,除了这⾥写的地址,其 他的URL都需要验证}));app.use(koaBody({// 允许上传多个文件multipart: true,formidable: {// 设置上传文件大小最大限制,默认2MmaxFileSize: 200 * 1024 * 1024,// 保留文件扩展名keepExtensions: true,}})
);/*router.routers()的作用是:启动路由router.allowedMethods()的作用是:允许任何请求(get,post,put)
*/
app.use(router.routes(), router.allowedMethods())
//路由重定向app.use(chance)app.listen(port, () => {console.log(`Server is running at http://localhost:${port}`)
})
三、Mysql搭建,navicat中操作
1、mysql存储emoji表情,数据添加外键
1、mysql可直接使用navicat创建
2、一个表中的数据可以为另一个表的外键,建立外键,就可实现改主表的数据,引用表的数据也跟随着一起改变,建立外键的条件,必须两个表的数据类型一样,数据编码一样,且建立时,引用的表中数值要么为空,要么填写的所有数据都可在主表中找到,也就是,在建立关系的时候主表包含引用表
3、在数据库中存储emoji表情时,存储的编码格式必须改为utf8mb4,因为表情的占用4个字节
utf8与utf8mb4的区别:
UTF-8编码中,一个英文字符占用一个字节的存储空间,一个中文(含繁体)占用三个字节的存储空间。
UTF8MB4:MySQL在5.5.3之后增加了utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。因此可以用来存储emoji表情。修改完数据库后,还需在后端配置连接时加上 charset:'utf8mb4'dbconfig.js
let config
//数据库配置config = {host:'localhost',port:'3306',user:'root',password:'1234',database:'back_project_2',//存入4字节的表情时,除了mysql改为utf8mb4,此处连接也需改为utf8mb4charset:'utf8mb4'}module.exports = config正常插入有两种方式
有特殊字符时需使用方式2
方式1
await queryback(`insert into artical (username,pagename,textpage,htmlpage) values ('${myusername}','${textname}','${textpage}','${htmlpage}')`)//此处必须这么写,因为文本可能中含有单引号,方式1写法会引起mysql的报错,
//而下面的方式2写法直接可以不用考虑单引号及其他特殊字符引起的变化方式2
await queryback(`insert into artical (username,pagename,textpage,htmlpage,comments) values (?,?,?,?,?)`, [myusername, name, textpage, htmlpage, desc])在配置连接池时,也无需过多的操作,只需加一个参数即可
const config = require('./dbconfig')
const mysql = require('mysql')//创建连接池
let pool =mysql.createPool(config);此处的p就是插入操作中的数组[myusername, name, textpage, htmlpage, desc]
function queryback(sql,p){return new Promise((resolve,reject)=>{pool.getConnection((err,connection)=>{if(err){reject(err)}else{//事件驱动回调connection.query(sql,p,(err,data)=>{if(err){reject(err)}else{resolve(data)}});//释放连接connection.release();}})}).catch((err)=>{console.log(err)})
}
module.exports = queryback
2、内连接
基础模型
1、
select * from user where '...'
2、
select * from user u
join test t on t.pagename = u.pagename where '...'
3、
select * from user u
join test t on t.pagename = u.pagename
join exam e on e.pagename = e.pagename
where '...'
4、
select 查询列表 from 表
[where 筛选条件]
group by 分组的字段
[order by 排序的字段];
5、
数据排序
升序 asc
降序 desc
6、将有同一个用户名的不同标签的表混合成只有一行,将标签内容整合成一行,类似于数组
例:select b.pagename,GROUP_CONCAT(b.classname SEPARATOR ',')
from textclass b
GROUP BY b.pagename7、混合示例
select a.pagename,a.username,a.writetime,a.comments,c.classname,d.id from artical a
join
(select b.pagename,
GROUP_CONCAT(b.classname SEPARATOR ',') classname
from textclass b
GROUP BY b.pagename) c
on a.pagename=c.pagename
join articalcollect d
on d.pagename = c.pagename
where d.username = 'test'
ORDER BY a.writetime DESC
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
