# 项目讲解
# 大文件上传
# 应用场景
上传文件大小有限制,页面响应时间过长会超时
# 解决方案
切片上传
# 思路
前端负责分块,服务端负责整合
前端文件对象File的原型链上面有slice方法,可以对文件进行分块。
前端对文件切片时,要标记序号保证后端有序地整合资源。
前端对文件分块之后,原来的一个文件对应一个请求就变成了一个文件对应多个请求了。所以,前端可以基于 Promise.all 将这多个接口整合,上传完成在发送一个合并的请求,通知服务端进行合并。
通过spark-md5根据文件内容计算出文件的hash值,方便做其他优化,比如:当 hash 值不变时,服务端没有必要重复读写文件(秒传)等.
合并时可通过 nodejs 中的读写流(readStream/writeStream),将所有切片的流通过管道(pipe)输入最终文件的流中。
如果某个切片上传失败了,服务端会返回当前分块失败的信息,其中会包含文件名称、文件hash、分块大小以及分块序号等,前端拿到这些信息后可以进行重传。
# 进度条数据
分块进度数据利用 axios 中的 onUploadProgress 配置项获取数据,通过使用computed 根据分块进度数据的变化自动自动计算当前文件的总进度。
# 断点续传
断点续传其实就是让请求可中断,然后在接着上次中断的位置继续发送,此时要保存每个请求的实例对象,以便后期取消对应请求,并将取消的请求保存或者记录原始分块列表取消位置信息等,以便后期重新发起请求。
# 秒传
所谓的秒传就是不用传,在正式发起上传请求时,先发起一个检查请求,这个请求会携带对应的文件 hash 给服务端,服务端负责查找是否存在一模一样的文件 hash,如果存在此时直接复用这个文件资源即可,不需要前端在发起额外的上传请求。
# 权限管理
# 路由权限
# 菜单栏权限
# 场景
不同级别用户看到不同菜单栏
# 思路
前端在用户登录后,根据后端返回的路由列表,使用addRoute方法动态添加路由
# 如何解决刷新页面,动态路由丢失
通过监听路由的变化,当刷新时,添加动态路由并定位到管理页面
// src/App.vue
watch: {
$route: {
async handler(newVal) {
console.log("newVal", newVal);
const role = localStorage.getItem(ROLE);
if (role && role === "admin") {
/* 在4.x版本中需手动调用router.replace方法重定向,
因为动态路由页面刷新时,matched的值为空;
在3.x版本中,刷新页面添加异步路由,matched有值,不需要再重定向 */
this.$router.addRoute("Layout", manage);
/* 在动态路由页面刷新时,matched数组为空 */
if (!newVal.matched.length && newVal.fullPath === "/manage") {
await this.$router.replace("/manage");
}
}
},
},
}
# 按钮权限
# 场景
根据不同的用户,一些页面功能进行显示或者隐藏
# 思路
在路由元信息上定义权限信息,通过自定义指令删除一些DOM节点
# 具体实现
- 定义路由元信息
{
path: "/about",
name: "About",
component: About,
meta: {
btnPermissions: ['admin']
},
}
- 增加判断方法
// src/utils/index.js
import { ROLE } from "../config/constant";
// 权限检查方法
export function has(value) {
let isExist = false;
// 获取用户按钮权限
let btnPermissionsStr = localStorage.getItem(ROLE);
if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
return false;
}
if (value.indexOf(btnPermissionsStr) > -1) {
isExist = true;
}
return isExist;
}
- 新增自定义指令
// src/main.js
app.directive("has", {
mounted(el) {
// 获取页面按钮权限
const btnPermissionsArr = router.currentRoute._value.meta.btnPermissions;
if (!has(btnPermissionsArr)) {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}
},
})
- 在需要的页面使用v-has
<template>
<div class="about">
<h1>This is an about page</h1>
<button type="button" v-has>管理员按钮</button>
</div>
</template>
# 前端水印
使用canvas绘制水印的内容。
水印的内容一般为登陆者的信息,这些信息从Vuex中读取。
将canvas导出为base64。
在body元素后面添加一个fixed布局的元素。该元素的background-image为刚刚导出的base64图片,将该元素的层级设置为9999,pointer-events:one。