如何根据后端返回的设备类型动态生成前端路由表
问题概述
最近在参与嵌入式设备前端项目, 这些设备通过挂载不同的传感器拥有不同的功能, 所以后台管理页面也不尽相同,这需要根据后端返回的功能列表来动态生成前端路由表.
由于项目直接套用的vue-cli脚手架, router类似这样在一开始就初始化写死了,而后端返回的routes则是在login页面里面通过axios去获取的,要等消息返回之后,用后端来的消息把路由表进行改写,以达到一套代码适应不同类型设备的要求.
new Vue({
el: '#app',
i18n,
router,
store,
render: h => h(App)
})
路由表中是写死的,首页默认会重定向到diviceManage界面
const routes=[
...
{
path: '/',
component: Layout,
redirect: '/diviceManage'
}
]
但是此时login的url已经生成,他拥有默认的重定向
首页的默认重定向
解决思路
最开始我试着这样,这时虽然可以用获取到的routes变量来改写前端路由表,点击登录依旧会跳到改之前设定的diviceManage界面,而不是新设定的newIndex,也就是说this.redirect = '/newIndex'
设置了没有起作用
const { cmd, routes } = response
this.$router.options.routes = routes
this.redirect = '/newIndex'
经过查找vue-router的issue, 发现这样一条https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Routes=[...]
const createRouter = () => new Router({
mode: 'history',
routes: Routes
})
const router = createRouter()
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher // the relevant part
}
export default router
router.matcher对外提供两个方法match(负责route匹配),addRoutes(动态添加路由),当路由进行切换时,内部会调用match方法const route = this.router.match(location, this.current)
对原始路由和要跳转的路由进行比对, 其实内部会使用matcher来匹配routes,修改router.matcher属性,新的routes就会替换旧的routes
在Router中定义Router函数,将resetRouter导出,在改了路由表之后再用新的matcher替换旧的matcher,这样,即使浏览器上面的url导航还是指向的旧地址,但是登陆之后,就会直接到跳转到newIndex了
const { cmd, routes } = response
this.$router.options.routes = routes
this.redirect = '/newIndex'
resetRouter()
这就解决了根据后端消息动态生成前端路由的问题
下面解决路由表持续存储的问题,由于路由表实在登录前动态获取的,而后管系统众多页面,不可能每个页面都写这样一套逻辑,所以在获取到routes就要进行存储,然后在各个页面加载前调用,这就解决了刷新导致路由表失效问题
localStorage.setItem('routes', JSON.stringify(routes))
但这也额外引出了一个本地存储在何时更新的问题,我是这样设计的,在获取到routes,除了写入localStorage,也向vuex中写入同名的变量,将路由表添加到vuex中,这样在不刷新的情况下,这个路由信息可以持续使用, 这样写有很多好处:
- 仅在首次登录时,由于vuex中没有routes变量,会触发getRoutes请求动态生成路由表,在其他情况下进入登录界面,比如修改ip,升级,重启等被踢出登录,则不会触发getRoutes函数,能减少请求
- 在非登陆页面,由于没有首页的重定向问题,页面刷新直接调用
localStorage.getItem('routes')
获取路由表,能减少请求,提高响应速度
mounted() {
if (!this.$store.app.routes) {
this.getRoutes()
}
},
methods: {
getRoutes(){
...
const { cmd, routes } = response
this.$router.options.routes = routes
this.redirect = '/newIndex'
resetRouter()
this.$store.dispatch('app/setRoutes', routes)
localStorage.setItem('routes', JSON.stringify(routes))
}
}