Bootstrap

vue3+ts之动态路由

vue3+ts之动态路由

本次分享的动态路由的思路是:本地只有基础的路由,其他路由全部由登录时根据后端返回的的数据中去获取,然后通过路由守卫拦截动态添加,权限全部交给后端控制,后端返回什么路由数据,前端页面就显示什么路由数据

准备工作

1.安装 vuex
npm install vuex@next -S
2.在src目录下新建 store 文件夹并新建 index.ts 文件

如图所示:
在这里插入图片描述

3、在main.ts 引入vuex

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// createApp(App).use(router).use(ElementPlus).mount('#app')
createApp(App).use(router).use(store).mount('#app')

一、登录时获取路由信息,然后并保存到本地(因为并没有真实的接口返回路由信息,此处我使用了模拟数据)

//登录接口
 login(data.ruleForm).then(res => {
            console.log('登录返回结果', res)
            localStorage.setItem('token', res.data.token)
            //模拟后端返回的路由信息,数据格式可以和后端商讨
            const list = [
              {
                path: '/goods',
                name: 'goods',
                meta: {
                  isShow: true,
                  title: '商品列表',
                },
                component: 'GoodsView.vue',
              },
              {
                path: '/user',
                name: 'user',
                meta: {
                  isShow: true,
                  title: '用户列表',
                },
                component: 'UserView.vue',
              },
              {
                path: '/role',
                name: 'role',
                meta: {
                  isShow: true,
                  title: '角色列表',
                },
                component: 'RoleView.vue',
              },
              {
                path: '/authority',
                name: 'authority',
                meta: {
                  isShow: false,
                  title: '权限列表',
                },
                component: 'AuthorityView.vue',
              },
              {
                path: '/ceshi',
                name: 'ceshi',
                meta: {
                  isShow: true,
                  title: '测试页面',
                },
                component: 'CeshiView.vue',
              },
            ]
            console.log('模拟动态路由数据', list)
            localStorage.setItem('routes', JSON.stringify(list))
            console.log('登录成功,跳转')
            router.push('/layout')
          })
          

二、router/index.js中渲染动态路由

1.未使用动态路由时的写法
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LayoutView from '../views/LayoutView.vue'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/layout',
    name: 'layout',
    component: LayoutView ,
    redirect: 'home',
    children: [
      {
        path: '/home',
        name: 'home',
        meta: {
          isShow: true,
          title: '首页',
        },
        component: () => import(/* webpackChunkName: "goods" */ '../views/GoodsView.vue'),
      },
       {
        path: '/goods',
        name: 'goods',
        meta: {
          isShow: true,
          title: '商品列表',
        },
        component: () => import(/* webpackChunkName: "goods" */ '../views/GoodsView.vue'),
      },
      {
        path: '/user',
        name: 'user',
        meta: {
          isShow: true,
          title: '用户列表',
        },
        component: () => import(/* webpackChunkName: "user" */ '../views/UserView.vue'),
      },
      {
        path: '/role',
        name: 'role',
        meta: {
          isShow: true,
          title: '角色列表',
        },
        component: () => import(/* webpackChunkName: "role" */ '../views/RoleView.vue'),
      },
      {
        path: '/authority',
        name: 'authority',
        meta: {
          isShow: false,
          title: '权限列表',
        },
        component: () => import(/* webpackChunkName: "Authority" */ '../views/AuthorityView.vue'),
      },
    ],
  },
  {
    path: '/login',
    name: 'login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "login" */ '../views/LoginView.vue'),
  },
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
})

router.beforeEach((to, from, next) => {
  const token: string | null = localStorage.getItem('token')
  if (!token && to.path !== '/login') {
    next('/login')
  } else {
    next()
  }
})

export default router
2.使用动态路由时的写法
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import LoginView from '../views/LoginView.vue'
import LayoutView from '../views/LayoutView.vue'
import store from '../store/index'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/login',
    name: 'login',
    component: LoginView,
  },
  {
    path: '/layout',
    name: 'layout',
    component: LayoutView,
    redirect: 'home',
    children: [
      {
        path: '/home',
        name: 'home',
        meta: {
          isShow: true,
          title: '首页',
        },
        component: () => import(/* webpackChunkName: "home" */ '../views/HomeView.vue'),
      },
    ],
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
})

router.beforeEach((to, from, next) => {
  // console.log('to, from', to, from);
  const isLoadRouters = store.state.asyncRoutesMark
  const token = localStorage.getItem('token')
  const routes = JSON.parse(localStorage.getItem('routes'))
  // 有token routes
  if (to.path == '/login') {
    next()
  } else {
    //用户已登录
    if (token && JSON.stringify(routes) != '[]') {
      if (isLoadRouters) {
        console.log('路由已添加,直接跳转到目标页面')
        next()
      } else {
        //解决刷新页面空白
        console.log('重新加载路由,并跳转到目标页')
        const routes = JSON.parse(localStorage.getItem('routes'))
        store.commit('setRouters', routes)
        store.commit('setAsyncRoutesMark', true)

        //添加路由
        routes.forEach(item => {
          // console.log('遍历', item)
          //此处我是添加到了layout下面,这个根据自己的项目情况而定
          router.addRoute('layout', {
            path: item.path,
            name: item.name,
            component: () => import(`../views/${item.component}`),
            meta: {
              title: item.meta.title,
              isShow: item.meta.isShow,
            },
          })
        })

        next({ ...to, replace: true })
      }
    } else {
      // console.log('无登录信息,跳转到登录页');
      store.commit('setMenuList', [])
      store.commit('setAsyncRoutesMark', false)
      next(`/login`)
    }
  }
})

export default router

三、vuex中写法-------store/inxes.ts


import { createStore } from 'vuex'
const state = {
  asyncRoutesMark: false,
  menu: [],
  routers: [],
}
const mutations = {
  setAsyncRoutesMark(state, data) {
    state.asyncRoutesMark = data
  },

  setMenuList(state, data) {
    state.menu = data
  },

  setRouters(state, data) {
    state.routers = data
  },
}


const options = {
  state,
  mutations,
}
const store = createStore(options)
export default store

动态路由不止当前这一种实现方法,如果此方法成功帮助了你,记得回来点个赞哦
;