vue3 + vite4 + vue-router4动态路由存在的问题

使用vite4、vue3、vue-router4搭建动态路由的时候遇到的问题及解决方法解决!!

我使用的是登录接口返回菜单,使用pinia存储菜单数据,使用pinia-plugin-persist加js-cookie进行持久化存储数据

以下是我运行正常的代码

  1. 存储登录信息的store(useLogin)
  // useLogin.js
  import { defineStore } from 'pinia'
  import { cookiesStorage } from '../utils'

  export const useLogin = defineStore("useLogin", {
      state() {
          return {
              token: '',
              menus: []
          }
      },
      getters: {
          getToken() {
              return this.token
          }
      },
      actions: {
          setLoginInfo({ token = '', menus = [] }) {
              this.token = token
              this.menus = menus
          }
      },
      persist: {
          enabled: true,
          strategies: [
              {
                  storage: cookiesStorage,
                  paths: ['token', 'menus']
              },
          ]
      }
  })
  1. utils.js
 // utils.js
  import Cookies from "js-cookie";
  const modules = import.meta.glob("../views/**/**.vue");

    export const cookiesStorage = {
        setItem(key, state) {
            return Cookies.set(key, state, { expires: 3 })
        },
        getItem(key) {
            return Cookies.get(key)
        }
    }

    /**
     * 
     * @param {Array} children chilren路由
     * @param {Object} item 当前路由
     * @returns import
     */
    function showRouterView(children, item) {
        let hasChildren = children.length > 0
        let allHidden = hasChildren && children.every(it => it.hidden)
        if (allHidden || children.length === 0) {
            return modules[`../views${item.path}.vue`]
        }
        return () => import(/* @vite-ignore */ `../components/parentRoute/parentRoute.vue`)
    }

    /**
     * 路由扁平化转换为tree
     * @param {*} routes // 路由
     * @param {*} pid // 对应父级的id,0为没有父路由
     */
    export function generatorRoute(routes, pid = 0) {
        let children = routes.filter(it => it.pid === pid)
        if (!children.length) {
            return []
        }
        let routers = children.map(item => {
            let children = generatorRoute(routes, item.id)
            let node = {
                name: item.name || "",
                path: item.path,
                meta: {
                    hidden: !!item.hidden,
                    title: item.title,
                    icon: item.icon || ""
                },
                component: showRouterView(children, item),
            }
            return {
                ...node,
                children
            }
        })
        return routers
    }
  1. 登录返回的数据
   [{
        id: 1,
        pid: 0,
        path: '/admin/userList',
        hidden: false,
        name: 'admin',
        icon: 'User',
        title: '用户管理'
    }, {
        id: 2,
        pid: 1,
        name: 'user',
        path: '/admin/userList',
        title: '用户列表'
    }, {
        id: 2,
        pid: 0,
        name: 'system',
        path: '/system/system',
        hidden: true,
        title: '系统管理'
    }]
  1. 静态路由
    import Layout from '@/layout/index.vue'
    export default [{
      path: '/login',
      name: 'login',
      component: () => import("@/views/Login.vue")
    },
    {
      path: '/',
      component: Layout,
      name: 'Layout',
      redirect: '/home/home',
      children: [{
        path: '/home/home',
        name: 'home',
        component: () => import("@/views/home/home.vue")
      }]
    },
    {
      path: '/:pathMatch(.*)*',
      name: 'notFund',
      component: () => import("@/views/notFund/notFund.vue")
    }]

5.permission.js

  // import pinia from '../stores/piniaInstance'

  import router from './index'
  import { useLogin } from '@/stores/useLogin'
  import { generatorRoute } from '../utils'
  const LOGIN_PATH = '/login'

  router.beforeEach((to, from) => {
      let store = useLogin()
      let menus = store.menus
      let token = store.token
      // 已登录,不是登录页面
      if (token && to.path !== LOGIN_PATH) {
          let hasRoute = menus.length > 0 && router.hasRoute(menus[0].name)
          // 没有添加过路由
          if (!hasRoute) {
              let routes = generatorRoute(menus)
              routes.forEach(it => {
                  router.addRoute("Layout", it)
              })
              router.replace(to.path)
          }
      } else if (token && to.path === LOGIN_PATH) {
          // 已经登录,是登录页面
          return { path: '/', replace: true }
      } else if (!token && to.path !== LOGIN_PATH) {
          // 没有登录,不是登录页面
          return { path: LOGIN_PATH, replace: true }
      }
  })
  1. piniaInstance.js
  import {createPinia} from 'pinia'
  import piniaPersist from 'pinia-plugin-persist'
  const pinia = createPinia()
  pinia.use(piniaPersist)
  export default pinia
  1. main.js
 import { createApp } from 'vue'
  import pinia from './stores/piniaInstance'
  import App from './App.vue'
  import router from './router'
  import '@/router/permission'
  const app = createApp(App)

  app.use(pinia)
  app.use(router)

  app.mount('#app')

以下是遇到的问题及解决的方法!!

一. 在permission.js中引入useLocation.js这些写会报错?

pinia.mjs:1690 Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
错误的代码:

 // permission.js
 import router from './index'
 import { useLogin } from '@/stores/useLogin'
 import { generatorRoute } from '../utils'
 const LOGIN_PATH = '/login'
 let store = useLogin()
 ... // 省略代码

原因是:这个时候使用的时候pinia实例还没有创建
解决方法:

  1. 第一种方法:把pinia实例作为第一参数传入useLogin方法中
    代码如下:
 // permission.js
   import pinia from '../stores/piniaInstance'
   import router from './index'
   import { useLogin } from '@/stores/useLogin'
   import { generatorRoute } from '../utils'
   const LOGIN_PATH = '/login'
   let store = useLogin(pinia)
   ... // 省略代码
  1. 第二种方法,把const store = useLogin()这段代码放到,router.beforeEach导航守卫中
    具体查看permission.js

二. 添加过动态路由后,刷新页面进入了404页面

  1. 解决方法:vue-router官方说明
  2. 使用router.replace(to.path)进行重定向

三. 动态导入路由组件的时候报错

vue-router.mjs:3451 Error: Unknown variable dynamic import: ../views/system/system.vue
错误代码:

  function showRouterView(children, item) {
      let hasChildren = children.length > 0
      let allHidden = hasChildren && children.every(it => it.hidden)
      if (allHidden || children.length === 0) {
          // 错误写法
          return () => import(/* @vite-ignore */`@/views${item.path}.vue`)
      }
      return () => import(/* @vite-ignore */ `../components/parentRoute/parentRoute.vue`)
  }

正确写法:
要使用import.meta.glob('../views/**/**.vue')把所有的路由组件放到import.meta.glob中
正确写法查看utils.js中的showRouterView,且要以.//开头,不能使用vite.config.js中配置的别名开头

posted @ 2023-02-27 10:05  sky_study  阅读(1541)  评论(0)    收藏  举报