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中,在实际项目应用的过程中应考虑数据存储的安全性。

如何实现路由列表解析?

  1. JSON格式的路由信息解析为JavaScript列表对象;
  2. 利用列表对象的filter方法实现解析函数,通过component判断是否为布局组件;
  3. 若为布局组件,使用布局组件代替component字符串;
  4. 若为具体页面,使用loadView函数加载对应的具体页面;
  5. 利用 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
// store/index.js
import router from '@/router'
import Main from '@/components/main'
import { getToken } from '@/lib/util'
import { getRoute } from '@/api/app'

const loadView = (viewPath) => {
 // 用字符串模板实现动态 import 从而实现路由懒加载
 return () => import(`@/view/${viewPath}`)
}

const filterAsyncRouter = (routeList) => {
 return routeList.map((route) => {
 if (route.component) {
 if (route.component === 'Main') {
 // 如果 component = Main 说明是布局组件
 // 将真正的布局组件赋值给它
 route.component = Main
 } else {
 // 如果不是布局组件就只能是页面的引用了
 // 利用懒加载函数将实际页面赋值给它
 route.component = loadView(route.component)
 }
 // 判断是否存在子路由,并递归调用自己
 if (route.children && route.children.length) {
 route.children = filterAsyncRouter(route.children)
 }
 }
 })
}

export default {
 state: {
 routeList: [],
 token: getToken(),
 hasGetRoute: false
 },
 mutations: {
 setRouteList(state, data) {
 // 先将 JSON 格式的路由列表解析为 JavaScript List
 // 再用路由解析函数解析 List 为真正的路由列表
 state.routeList = filterAsyncRouter(JSON.parse(data))
 // 修改路由获取状态
 state.hasGetRoute = true
 }
 },
 atcions: {
 getRouteList({ state, commit }) {
 return new Promise((resolve) => {
 const token = state.token
 getRoute({ token }).then((res) => {
 let data = res.data.data
 // 注意这里取出的是 JSON 格式的路由列表
 commit('setRouteList', data)
 resolve()
 })
 })
 }
 }
}

常见问题

页面卡在登录页面而且不断刷新

这个问题的解决方案在「实现代码」中已经提到了,只需要在判断登录状态的时候注意不要将两种未登录状态混为一谈即可。但这样治标不治本,因为同样的问题可以由不同形式的代码导致,那导致问题的原因是什么那?然我们慢慢分析:

我们先假设不小心把两种未登录的状态混在一起判断:

if (!token) {
 next({ name: LOGIN_PAGE_NAME })
}

这里的next({ name: LOGIN_PAGE_NAME })方法会再一次激活全局前置守卫,从而导致再一次进入判断并触发next({ name: LOGIN_PAGE_NAME }),如此递归调用下去,页面就会卡主并且不断刷新。

动态路由配合路由懒加载

实现这一目的的方案也在代码示例中展示了:

const loadView = (viewPath) => {
 return () => import(`@/view/${viewPath}`)
}

这里是运用了一个 JavaScript 不太常用的特性:字符串模板,使用此特性让不支持字符串拼接的import操作能够实现动态import不同的模块。

动态路由刷新后 404

这应该是本方案中最常见的一个错误之一,其原意是很多人在创建「基本静态路由」的时候回把 404 页面的路由也加入在里面,从而导致页面加载初期动态路由还没有加入到路由实例中,匹配范围最广的 404 页面就会跳出来。解决方法就是将 404 页面的路由也加入到动态路由中。

动态路由刷新后变空白页

造成这一问题的原因有很多,我这里遇到的问题是使用参考文章3解决的,但具体原理我还没弄清楚,等我做一下研究再来更新。

动态路由页面刷新时 Title 不稳定

造成这一问题的原因很简单:因为页面刷新的时候路由信息还没加载进来,所以根本没有标题信息可供加载。但是我还没找到比较好的解决方案,同样等我研究一下再更新。

参考大师兄:

Vue 动态路由的实现……

Vue Router 文档页面

rambo:vue router 动态路由 刷新后变空白页

总结

到此这篇关于Vue Router 实现动态路由和常见问题及解决方法的文章就介绍到这了,更多相关vue router 动态路由内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 讲解vue-router之什么是动态路由

    前言: 今天我来给大家说道说道v-router,这是个什么东西?我们先从动态路由讲起. GitHub:https://github.com/Ewall1106/mall/tree/master 1.动态路由有一个什么适用场景呢? 比如在写商品详情页面的时候,页面结构都一样,只是商品id的不同,所以这个时候就可以用动态路由动态. 2.官方文档 首先我们来看看官方文档上是怎么解释动态路由的?(https://router.vuejs.org/zh-cn/) 你可以在一个路由中设置多段"路径参数&qu

  • vue router学习之动态路由和嵌套路由详解

    本文主要参考:https://router.vuejs.org/zh-cn/essentials/nested-routes.html 本文的阅读前提是已经能够搭建一个vue前台程序并且运行.如果还么有搭建可以参考文章: http://www.jb51.net/article/111650.htm 好,下面上货. 首先介绍一下动态路由. 动态路由按照我的理解,就是说能够进行页面的跳转,比如说:下面的这个页面中: <template> <div id="app">

  • vue router动态路由设置参数可选问题

    在日常工作中,我们需要将匹配到的所有路由,映射到一个组件上. 如下代码想要达到的效果: 不传page和id,则映射到user默认list页面 传page和id,根据page不同,显示不同的页面 问题 使用以下代码片段是不能实现以上效果的,因为默认情况下page和id参数是必传的,如果不传参数,则会根据默认路由跳转到home页面 new Router({ routes: [ { path: '/user/:page/:id', name: 'User', component: () => impo

  • 详解vue-router 动态路由下子页面多页共活的解决方案

    我们都知道 vue-router 的动态路由匹配对组件是原地复用的策略,需要我们在组件中根据不同的 $route 参数展示不同的数据,这在大部分情景下是很高效的做法,但这无疑增加了组件的复杂度,而且不同参数间切换因为是同组件复用,切换效果不加修饰的话会显得很生硬,这里放一张图片感受一下. 如果我们希望能够每个动态参数都能渲染出一个组件而不是去复用怎么办呢? 我这里提供一个简便的方案 通常动态路由我们都是用来处理详情页 const router = new VueRouter({ routes:

  • 详解vue-router2.0动态路由获取参数

    一下demo演示2.0中的vue-router是如何获取到不同参数的,并在地址栏中匹配不同的信息 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript" src="vue.js&qu

  • 全面解析vue router 基本使用(动态路由,嵌套路由)

    路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容.Home按钮  => home 内容, about按钮 => about 内容,也可以说是一种映射. 所以在页面上有两个部分,一个是点击部分,一个是点击之后,显示内容的部分. 点击之后,怎么做到正确的对应,比如,我点击home 按钮,页面中怎么就正好能显示home的内容.这就要在js 文件中配置路由. 路由中有三个基本的概念 route,

  • vue-router之nuxt动态路由设置的两种方法小结

    方法一:router-link <div class="slide-item" v-for="user in shareData.users"> <nuxt-link :to="'/community/member/'+ user.id"> <img src="../../static/head.png" alt=""> <p>{{user.nickname}

  • vue router动态路由下让每个子路由都是独立组件的解决方案

    vue-router 之动态路由 vue-router官网上面是这样说的 // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) 然后,我就这样写了: this.$router.push({path:'manage', query: {id: 'tasklist'}})1 结果很明显,失败了.然后我就默默的再次看了一下官网,结果发现了这句话 // 命名的路由 r

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

    个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表.常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单. 如何利用Vue Router 实现动态路由 Vue项目实现动态路由的方式大体可分为两种: 前端将全部路由规定好,登录时根据用户角色权限来动态展示路由: 路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染: 第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开.第二种方式现

  • Vue Router的懒加载路径的解决方法

    单页应用产出的入口chunk大小随着业务的复杂度线性增加,导致后期加载速度越来越慢.后面就需要对不同路径下的模块进行拆分,打包到相应的chunk下,按需加载,找到chunk的大小.个数和页面加载速度的平衡点. 解决办法 .vue模块文件按需加载,其实要做到两件事情:一是标记出这是一个异步组件:二是通知webpack把该组件单独产出为一个chunk. vue的异步组件 官网给出的异步组件写法:异步组件是一个函数,函数的返回值是一个Promise,只是Promise的resolve函数的参数是常规组

  • vue系列之动态路由详解【原创】

    开题 最近用vue来构建了一个小项目,由于项目是以iframe的形式嵌套在别的项目中的,所以对于登录的验证就比较的麻烦,索性后端大佬们基于现在的问题提出了解决的方案,在看到他们的解决方案之前,我先画了一个比较标准的单系统的解决方案. 本文目录: 一: 设想 二: 讨论 三:实现 四:总结 一: 设想 简单解释下上图就是: 首先前端从cookie获取token,如果没有token就跳转到登录页面登录,登录验证之后生成token存在数据库中并返回给前端:前端将这个token保存下来,为了让在浏览器新

  • vue iview实现动态路由和权限验证功能

    github上关于vue动态添加路由的例子很多,本项目参考了部分项目后,在iview框架基础上完成了动态路由的动态添加和菜单刷新.为了帮助其他需要的朋友,现分享出实现逻辑,欢迎一起交流学习. Github地址 iview-dynamicRouter 实现目标 客户端从服务端拿到路由和权限数据后,刷新项目的路由和菜单列表,并进行权限控制. 项目基础 基础框架: iview组件库官方模板项目 iview-admin 的template分支项目,此项目为 iview-admin 的基础框架代码.项目地

  • Vue 配合eiement动态路由,权限验证的方法

    1.要实现动态路由,只需要在main.js中将所有路由表先规定好,如下 const routes=[ {path:'/login',component:login},/*登录*/ {path:'/home',component:home},/*首页*/ {path:'/monitor',component:monitor},/*实时监控*/ {path: "/orderQuery", component: orderQuery},/*电子围栏*/ {path: "/fence

  • vue router 传参获取不到的解决方式

    在当前路由中有一个toArticle方法可以跳转到article页面 methods:{ toArticle:function(index) { this.$router.push({path:'/article',params:this.blogList[index]}); } } 在article中接受不到params mounted(){ console.log(this.$route.params) //这里输出undifined } 导致这样的原因是因为params需要通过name来获

  • vue中监听路由参数的变化及方法

    在vue项目中,假使我们在同一个路由下,只是改变路由后面的参数值,期望达到数据的更新. mounted: () =>{ this.id = this.$route.query.id; this.getdetail() } getDetail()方法中会用到this.id这个参数,在同一页面切换id的值,并不会触发vue的声明周期函数. 可以添加路由监听: watch: { $route: { handler() { this.id = this.$route.query.id; this.get

  • vue中实现动态生成二维码的方法

    最近项目中有个裂变分享的需求,需要在页面中根据分享人的身份动态生成二维码图片放置在页面中,所以研究了一下这个功能的实现,同时把实现过程记录如下: 1.引入二维码生成模块 npm install qrcodejs2 --save 注意:此处安装qrcodejs2,安装依赖后可在main方法中进行全局引用设置,也可单独某个页面中进行引用设置. 2.引入使用 import QRCode from 'qrcodejs2'; 备注:在main中设置全局可使用 Vue.prototype.qrCode 3.

  • Vue项目中使用addRoutes出现问题的解决方法

    目录 前言 一.404页面 1. 出现的原因 2. 解决方案 二.刷新白屏 1. 出现原因 2. 解决方案 三.路由重复 1.  出现原因 2. 解决方案 总结 前言 addRoutes官方介绍: 函数签名: router.addRoutes(routes: Array<RouteConfig>) 动态添加更多的路由规则.参数必须是一个符合 routes 选项要求的数组. 这两天做vue后台权限管理系统的时候,发现使用vue提供的addRoute添加路由以后,会出现两个bug,一起来看看如何解

随机推荐