vite+vue3+pinia实现动态注册懒加载路由教程
作者:Tt涛㏒
这篇文章主要介绍了vite+vue3+pinia实现动态注册懒加载路由教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
模块版本
- vite:^5.4.10
- vue:^3.5.12
- vue-router:^4.4.5
- pinia:^2.2.6
- pinia-plugin-persistedstate:^4.1.3(pinia持久化插件)
业务需求说明
每个程序都要满足不同的业务需求,以下代码只是满足我当前的业务需求
- 后端保存了每个用户的前端路由列表信息
- 前端默认路径为’/login’,比如www.xxxx.com访问默认显示login页面
- 前端默认注册静态路由’/login’,并重定向到路径’/’
- 用户点击登录后获取路由列表,通过addRoute注册路由
router代码
import {createRouter, createWebHistory} from 'vue-router' import type {Router} from 'vue-router' const router: Router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', component: () => import('@/views/Login.vue') }, { path: '/login', name: 'login', meta: { navBar: false, token: false }, component: () => import('@/views/Login.vue') } ], }) export default router
Login页面(ts+setup)
<script lang="ts" setup> import {ref} from "vue" import {useRouter} from 'vue-router' import {loginApi} from '@/api/login/loginApi' import {useUserInfo, useMyToken} from '@/stores/counter' import type {RouterListItem} from '@/types/login' const userInfo = useUserInfo() const myToken = useMyToken() const router = useRouter() /** 用户输入账号 */ const userName = ref<string>('') /** 用户输入的密码 */ const password = ref<string>('') /** 登录API */ const logon = async () => { /** 发送登录请求,携带用户账号和密码信息 */ loginApi(userName.value, password.value).then((res) => { /** 后端返回的用户信息和路由列表,写入pinia */ userInfo.setUserInfo(res.result.info) /** 用户token信息,写入pinia */ myToken.setMyToken(res.result.token) /** 调取pinia内的方法注册路由 */ userInfo.registeredRoute() /** 由于后端返回的路由列表是JSON格式,需要转换 */ let routerListArr: RouterListItem[] = [] if (userInfo.userInfo !== undefined) { /** 转换路由信息 */ routerListArr = JSON.parse(userInfo.userInfo?.router_list) } /** 跳转到用户路由列表中的第一个路由 */ router.replace(routerListArr[0].path) }, (err) => { /** 错误提示 */ ElMessage({ message: err.response.data.message, type: 'error' }) }) } </script>
pinia代码
import {ref} from 'vue' import {defineStore} from 'pinia' import type {RouterListItem, UserInfo} from '@/types/login' /** 导入的是自己的路由文件 src-router-index.ts */ import myRouter from '@/router/index' /** 用户信息相关 */ export const useUserInfo = defineStore( 'userInfo', () => { /** 用户信息 */ const userInfo = ref<UserInfo | undefined>(undefined) /** 存储用户信息方法 */ const setUserInfo = (info: UserInfo): void => { userInfo.value = info } /** 清除用户信息方法-用于退出登录或token验证失败 */ const clearUserInfo = (): void => { userInfo.value = undefined } /** 注册路由方法 */ const registeredRoute = () => { /** 判断userInfo.value是否为undefined,不是则进入 */ if (userInfo.value !== undefined) { /** 由于我的后端返回路由列表是JSON格式,需要转换后使用 */ const routerListArr: RouterListItem[] = JSON.parse(userInfo.value?.router_list) /** 通过循环添加路由 */ for (const item of routerListArr) { const route = { path: item.path, name: item.name, meta: { navBar: item.meta.navBar, token: item.meta.token }, /** 懒加载导入vue页面 由于我的后端返回的component名称是带有后缀名的,直接导入vite会报警告,虽然不影响使用,但是安全起见 还是替换了为好,这里使用replace将.vue替换为空 */ component: () => import(`../views/${item.component.replace('.vue', '')}.vue`) } myRouter.addRoute(route) } } } return { userInfo, setUserInfo, clearUserInfo, registeredRoute } }, { /** pinia持久化 */ persist: true }) /** 用户token相关 */ export const useMyToken = defineStore( 'myToken', () => { /** token信息 */ const myToken = ref<string | undefined>(undefined) /** 存储token信息方法 */ const setMyToken = (token: string) => { myToken.value = token } /** 清除token信息方法-用于退出登录或token验证失败 */ const clearMyToken = () => { myToken.value = undefined } return { myToken, setMyToken, clearMyToken } }, { /** pinia持久化 */ persist: true })
关于替换.vue后缀的说明
导入必须以 或 开头。./…/
所有导入都必须相对于导入文件开始。导入不应以变量、绝对路径或裸导入开头:
// Not allowed import(bar); import(`${bar}.js`); import(`/foo/${bar}.js`); import(`some-library/${bar}.js`);
导入必须以文件扩展名结尾
文件夹可能包含您不打算导入的文件。因此,我们要求导入在导入的静态部分中以文件扩展名结尾。
// Not allowed import(`./foo/${bar}`); // allowed import(`./foo/${bar}.js`);
导入到您自己的目录必须指定文件名模式
如果您导入自己的目录,则最终可能会得到您不打算导入的文件,包括您自己的模块。因此,需要提供更具体的文件名模式:
// not allowed import(`./${foo}.js`); // allowed import(`./module-${foo}.js`);
详细信息见文档:dynamic-import-vars#limitations
main代码(vue的main.ts文件内)
import './assets/main.css' import {createApp} from 'vue' import App from './App.vue' import router from './router' /** 导入createPinia用于创建pinia */ import {createPinia} from 'pinia' /** 导入ElementPlus */ import ElementPlus from 'element-plus' /** 导入ElementPlus国际化文件-中文 */ import zhCn from 'element-plus/es/locale/lang/zh-cn' /** 导入pinia持久化插件 */ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' /** 导入pinia内的useUserInfo,用于动态注册路由 */ import {useUserInfo} from '@/stores/counter' /** 导入el-loading样式 */ import 'element-plus/theme-chalk/el-loading.css' /** 导入el-message样式 */ import 'element-plus/theme-chalk/el-message.css' import type {Pinia} from 'pinia' const app = createApp(App) /** 创建pinia */ const pinia: Pinia = createPinia() /** 注册piniaPluginPersistedstate持久化插件 */ pinia.use(piniaPluginPersistedstate) /** 注册pinia */ app.use(pinia) /** 获取pinia内的userInfo,用于动态注册路由 */ const userInfo = useUserInfo() /** 动态注册路由,并且防止刷新页面导致路由丢失 这里直接调用userInfo.registeredRoute()会不生效,需要写到函数内部,再调用函数 并且需要写在app.use(router)之前 */ const addRouter = () => { userInfo.registeredRoute() } addRouter() app.use(router) /** 注册ElementPlus,并使用中文模式 */ app.use(ElementPlus, { locale: zhCn, }) app.mount('#app')
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。