[ 思路 ]
- 游客,会员,管理员,不同身份对应的页面数量是不同的,所以再token中要对角色身份和权限进行标记
- 服务端需要对前端页面进行权限统筹,针对不同权限的用户,返回相应权限范围的页面和导航数据。
- 前端收到与用户权限相对应的页面和导航数据后,进行匹配渲染。
- 在导航守卫中加入资源预请求,根据请求到的页面和信息,动态的添加路由上去。
router.beforeEach(async (to, form) => {
// 防抖,当添加了动态路由以后,就不会再重复添加
const state = router.getRoutes().find(r => r.name === 'home').children.length
if(state) return
// 白名单,登陆和注册页不进行动态路由添加
if(to.path === '/login' || to.path === '/register') {
return true
} else {
// 在该方法里面进行资源内容的缓存和提取,
// 在缓存对象里面存放一个包含 token 和 data 属性的对象,
// 检测该对象的 token 和 登陆后存储的 token 是否一致,
// 一致就返回该对象的 data,否则从服务端获取 data,并将 token 和 data 同时写入该对象缓存起来。
// 每次登陆后,token都会改变,资源将重新获取,否则从缓存获取。
// 如果令牌过期,会跳转到登录页。资源同样将被重新缓存
const {status, msg, data} = await getResource()
if(status === 2) {
// 处理路由配置
const homeRoute = routes.find(r => r.name === 'home')
homeRoute.children = []
data.forEach((d, i) => {
homeRoute.children.push({
path: d.path,
name: d.name,
components: {
home: comp[d.name]
}
})
if(i==0) {
homeRoute.children.push({
path: '',
name: d.name,
components: {
home: comp[d.name]
}
})
}
})
// 添加动态路由
router.addRoute(homeRoute)
// 重点,页面刷新会导致动态路由会消失,页面找不到路由出现白屏。
// 刷新的时候,to.redirectedFrom 属性为空字符串,由此判断是刷新或者从其他站点直接跳转过来
// 非空的话,说明是站内跳转过来的,正常情况下一定是已经加载了动态路由的。不会出现白屏问题
if(to.redirectedFrom) {
// 这里判断访问路径是否是站内注册了路由的页面路径
// 如果能获得 name 属性,说明是合法路径,放行即可
if(to.name) {
return true
}
// 否则是非法路径,重定向去站点首页即可
return {path: '/'}
} else {
// to.redirectedFrom 属性为空字符串,页面白屏,那么就重复一次路由跳转。
// 当重复跳转到达页面的时候,动态路由已经被再次添加,页面正常显示。
// 注意前面的两轮判断非常重要,如果没有的话,页面会进入重复跳转的死循环。
return {...to, replace: true}
}
}else {
// 获取资源失败,说明令牌认证有问题,跳转到登陆页面去登陆
return {name: 'login'}
}
}
})
[ 性能优化 ]
- 上述操作有一个问题,就是每次路由跳转都会去重复预请求页面数据,造成资源浪费
- 通过缓存处理进行性能优化
- 参考
// http/index.js
// 这里我目前不会 pinia,所以用 localStorage 进行了缓存
export const getResource = async function() {
const resource = window.localStorage.getItem('resource')
if(resource) {
const source = JSON.parse(resource)
if(source.token === window.localStorage.getItem('token')) {
return source.data
}
}
const data = await $.get('/resource')
// 这里我从服务端返回的 状态码 一律是200,而在返回数据对象内部自己重新定义了 status 属性12345,所以是 2
if(data.status !== 2) return []
const source = {
token: window.localStorage.getItem('token'),
data
}
window.localStorage.setItem('resource', JSON.stringify(source))
return res(data)
}