Vue Router 实现动态路由和常见问题及解决方法

个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表。常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单。

如何利用Vue Router 实现动态路由

Vue项目实现动态路由的方式大体可分为两种:

前端将全部路由规定好,登录时根据用户角色权限来动态展示路由;

路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染;

第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开。第二种方式现在来说也比较常见了,因为近期项目正好用到所以单独讲一下,这里我使用的方案是利用Vue Router的一些特性实现后端主导的动态路由。

使用到的功能特性

Vue Router 全局前置守卫

这里我们主要借助全局前置守卫的「前置」特性,在页面加载前将当前用户所用到的路由列表注入到Router实例中,注入使用到的方法则是下面的router.addRoutes方法。

Vue Router router.addRoutes 实例方法

router.addRoutes方法可以为Router实例动态添加路由规则,刚好为我们实现动态路由提供了注入方法。

Vue Router 路由懒加载

懒加载这个功能不是动态路由的必要功能,但既然提供了这一特性,所以就直接在项目中使用了。

具体思路

基础信息准备

前端代码实现基本静态路由,例如:登录页路由,服务器错误页路由等(这里有一个坑,后面讲)。数据库存储全部动态路由信息。

数据库如何存储动态路由信息?我选择的方案是现将路由引用的对象字符串化,再将路由列表转化为JSON格式传输给后端,经后端处理后存储到数据库里。总之在前后端进行传递的是JSON格式的路由列表信息。

如何将路由中引用的对象字符串化?我遇到的实际问题是:使用的UI组件提供了布局方案,需要引用布局组件并在子路由处引用具体页面。我选择的解决方案是:区别对待需要引用布局组件的component属性,使用简短字符串代替布局组件,使用文件路径字符串代替页面引入。具体实现可以看后面的代码实例。

利用全局前置守卫对路由信息进行判断

1-判断用户是否登录1.1-若未登录,跳转至登录页面1.2-若已经登录,判断是否已获取路由列表1.2.1-若未获取,从后端获取、解析并保存到Vuex中1.2.2-若已获取,跳转至目标页面

这里我没做太多考察,直接将取到数据存储到了Vuex中,在实际项目应用的过程中应考虑数据存储的安全性。

如何实现路由列表解析?

将JSON格式的路由信息解析为JavaScript列表对象;

利用列表对象的filter方法实现解析函数,通过component判断是否为布局组件;

若为布局组件,使用布局组件代替component字符串;

若为具体页面,使用loadView函数加载对应的具体页面;

利用 router.addRoutes 方法动态添加路由

这一步就很简单了,将解析好的路由列表通过router.addRoutes方法添加到Router实例中即可。

简单的实现代码

// router/index.js import Vue from 'vue' import store from '@/store' import Router from 'vue-router' import { getToken } from '@/lib/util' Vue.use(Router) // 定义静态路由 const staticRoutes = [ { path: '/login', name: 'login', meta: { title: '登录页面', hideInMenu: true }, component: () => import('@/view/login/login.vue') }, { path: '/401', name: 'error_401', meta: { hideInMenu: true }, component: () => import('@/view/error-page/401.vue') }, { path: '/500', name: 'error_500', meta: { hideInMenu: true }, component: () => import('@/view/error-page/500.vue') } ] // 定义登录页面名称(为了方便理解才定义的) const LOGIN_PAGE_NAME = 'login' // 实例化 Router 对象 const router = new Router({ staticRoutes, mode: 'history' }) // 定义全局前置守卫(里面有两个坑要注意) router.beforeEach((to, from, next) => { // 通过自定义方法获取用户 token 用来判断用户登录状态 const token = getToken() if (!token && to.name !== LOGIN_PAGE_NAME) { // 如果没有登录而且前往的页面不是登录页面,跳转到登录页 next({ name: LOGIN_PAGE_NAME }) } else if (!token && to.name === LOGIN_PAGE_NAME) { // 如果没有登录而且前往的页面是登录页面,跳转到登录页面 // 这里有一个坑,一定要注意这一步和上一步得分开写 // 如果把前两步判断合并为 if (!token) next({ name:login }) // 则会形成登录页面无限刷新的错误,具体成因后面解释 next() } else { // 如果登录了 if (!store.state.app.hasGetRoute) { // 如果没有获取路由信息,先获取路由信息而后跳转 store.dispatch('getRouteList').then(() => { router.addRoutes(store.state.app.routeList) // 这里也是一个坑,不能使用简单的 next() // 如果直接使用 next() 刷新后会一直白屏 next({ ...to, replace: true }) }) } else { // 如果已经获取路由信息,直接跳转 next() } } }) export default router

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/d3deba13a69365128328028d5e667f96.html