09、vue3动态路由搭建

1、为什么需要动态路由?

一般开发都是写静态路由,我们为什么要使用动态路由呢?因为动态路由对权限的划分是一个最有效的解决方法,下面我们就开始搭建一个动态路由的项目,使用技术是vite+ts+vue3+pinia+mock,mock主要用于模拟请求接口之后的处理,更接近实际项目

2、创建一个vite项目

yarn create vite

创建一个项目之后启动,具体启动过程初始化命令里面都会有提示的这里就不详细讲解了,删除里面的HelloWord.vue文件,这样一个空白项目就有了,下面我们先进行安装需要的插件

3、插件安装

需要安装vue-routerpiniaaxiosmock,我这边是使用的yarn安装的,如果使用npm安装也是可以的

//安装vue-router和axios、pinia
yarn add vue-router axios pinia
//安装mockjs和vite-plugin-mock。mock主要用途仅为模拟后端数据接口,所以安装为开发依赖,若打包为生产环境则会失效。
yarn add mockjs vite-plugin-mock

安装好插件之后,开始创建文件夹以及需要的文件

4、创建文件夹以及文件

4.1 在src下面新建router/index.ts:

import { RouteRecordRaw, createRouter, createWebHistory,createWebHashHistory } from 'vue-router'

// 静态路由表
const routes: Array<RouteRecordRaw> = [
    {
        // 路由重定向配置
        path: '/',
        redirect: '/Home'
    }, {
        path: '/Home',
        component: () => import('../views/Home.vue')
    }
]

// 路由对象
const router = createRouter({
    history: createWebHashHistory(),//hash路由
    routes
})

export default router

4.2 打开app.vue文件,改成如下代码:

<template>
  <router-view></router-view>
</template>

<script setup lang="ts"></script>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

4.4 在main.ts里面引入

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' //引入router

const app=createApp(App)

app.use(router) //使用router

app.mount('#app')

这样就可以看到home页面了


image.png

4.5 模拟mock数据使用

4.5.1 创建/src/mock/index.ts

// /src/mock/index.ts
import { MockMethod } from "vite-plugin-mock"

const mock: Array<MockMethod> = [
    {
      url: '/api/test',
      method: 'get',
      response: () => {
          return {
              status: 200,
              message: 'success',
              data: '返回的数据'
          }
      }
    }
]

export default mock

4.5.2 配置vite.config.ts

要想使用mock,我们还需要在vite.config.ts文件下对mock进行配置,让vite启动的同时启动mock服务。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
 
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    // mock服务
    viteMockServe({
      supportTs: false,
      logger: false,
      mockPath: "./src/mock/",
    }),
  ]
})

4.5.3 配置axios

/src/utils/request.ts:此文件为axios配置文件,它将创建一个axios全局单例,由于本项目仅做最简单的演示,所以仅配置baseUrl,实际使用时可根据实际情况添加拦截器等功能。

import axios from 'axios'
// axios对象
const service = axios.create({
    // axios请求基础URL
    baseURL: "http://127.0.0.1:5175", //此处根据自己启动的端口来
    timeout: 5000
})
 
export default service

/src/apis/index.ts:此文件为接口文件,接口统一放到此文件中。

import req from '../utils/request'
 
/**
 * 测试接口
 */
 
// 测试用Hello World
export const TestApi = () => req({ url: '/api/test', method: 'get' })

home.vue代码修改如下

<template>
    <h1>Home</h1>
</template>
 
<script lang="ts" setup>
import { TestApi } from '../apis'
 
TestApi().then(res => console.log(res)).catch(err => console.log(err))
</script>

在这里就可以看到网络里面有接口请求成功了。

5、配置动态路由

5.1 配置动态路由接口

首先我们先在刚刚创建的mock接口文件/src/mock/index.ts中添加一个返回路由信息的路由接口,如下所示

import { MockMethod } from "vite-plugin-mock"

const mock: Array<MockMethod> = [
    {
      url: '/api/test',
      method: 'get',
      response: () => {
          return {
              status: 200,
              message: 'success',
              data: 'Hello World'
          }
      }
    },
    {
      url: '/api/getRoutes',
      method: 'get',
      response: () => {
        const routes = [
          {
              path: '/Page1',
              name: 'Page1',
              component: 'Page1/index'
          },
          {
              path: '/Page2',
              name: 'Page2',
              component: 'Page2/index'
          }
        ]
        return {
            status: 200,
            message: 'success',
            data: routes
        }
      }
    }
]

export default mock

/src/api/index.ts

import req from '../utils/request'

export const TestApi = () => req({ url: '/api/test', method: 'get' })

export const GetRoutes = () => req({ url: '/api/getRoutes', method: 'get' })

5.2 安装并配置pinia

piniavuex都是vue的全局状态管理工具,在前面的步骤已经安装了这个插件,我们就直接使用。
/src/store/index.ts代码如下

// /src/store/index.ts
import { defineStore } from 'pinia'
import { RouteRecordRaw } from 'vue-router'

//引入所有views下.vue文件 
let modules = import.meta.glob("../views/**")

// pinia状态管理器
export const useStore = defineStore('myStore', {
    state: () => {
        return {
            // 路由表
            routes: [] as Array<RouteRecordRaw>
        }
    },
    getters: {},
    actions: {
        // 添加动态路由,并同步到状态管理器中,这个地方逻辑是写的最简单的方式,大家可以根据自己的业务需求来改写,本质就是使用addRoute来实现
        addRoutes(data: Array<any>, router: any) {
            data.forEach(m => {
                this.routes.push({
                    path: m.path,
                    name: m.name,
                    // 错误示例:components:()=>import(`../views/Pages/${m.component}`)
                    // 正确示例如下:
                    component: modules[`../views/${m.component}.vue`],
                })
            })
            console.log('this.routes',this.routes)
            this.routes.forEach(m => router.addRoute(m))
        },
    }
})

main.ts代码如下

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
 
import { createPinia } from 'pinia'
const pinia = createPinia()
 
const app = createApp(App)
 
// 启用路由
app.use(router)
 
// 启用pinia
app.use(pinia)
 
app.mount('#app')

/src/views/home.vue代码如下

<template>
  <h1>Home</h1>
  <div style="display: flex; gap: 20px">
    <button v-for="item in routes" @click="handleClick(item.path)">
      {{ item.name }}
    </button>
  </div>
</template>

<script lang="ts" setup>
import { useStore } from "../store";
import { TestApi, GetRoutes } from "../apis";
import { useRouter } from "vue-router";
import { computed } from "@vue/reactivity";
import { onMounted } from "vue";

const router = useRouter();
const store = useStore();

// 动态路由表
const routes = computed(() => store.routes);

// 路由按钮点击事件
const handleClick = (path: string) => {
  router.push({ path });
};

onMounted(() => {
  if (store.routes.length < 1) {
    // 获取动态路由
    GetRoutes().then((res) => {
      store.addRoutes(res.data.data, router);
    });
  }

  // 测试接口
  TestApi()
    .then((res) => console.log(res.data))
    .catch((err) => console.log(err));
});
</script>

这样就实现了动态路由的跳转,但是这样直接刷新page1页面就会导致页面空白,所以我们是在路由拦截里面进行了处理

import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router'
import { useStore } from "../store";
import { GetRoutes } from '../apis'

// 静态路由表
const routes: Array<RouteRecordRaw> = [
    {
        // 路由重定向配置
        path: '/',
        redirect: '/Home'
    }, {
        path: '/Home',
        component: () => import('../views/Home.vue')
    }, {
        // 404页面配置
        path: '/:catchAll(.*)',
        component: () => import('../views/404.vue')
    }
]

// 路由对象
const router = createRouter({
    history: createWebHashHistory(),
    routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
    if (to.path !== '/Home' && to.path !== '/') {
        const store = useStore()
        if (store.routes.length < 1) {

            GetRoutes().then(res => {
                store.addRoutes(res.data.data, router)
                next({ path: to.path, replace: true })

            }).catch(_ => {
                next()
            })

        } else {
            next()
        }
    } else {
        next()
    }
})

export default router

这样动态路由就搭建成功了。


image.png

image.png

参考文章

https://blog.csdn.net/XUMENGCAS/article/details/128036487

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 175,490评论 5 419
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 74,060评论 2 335
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 124,407评论 0 291
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 47,741评论 0 248
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 56,543评论 3 329
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 43,040评论 1 246
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 34,107评论 3 358
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 32,646评论 0 229
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 36,694评论 1 271
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 32,398评论 2 279
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 33,987评论 1 288
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 30,097评论 3 285
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 35,298评论 3 282
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 27,278评论 0 14
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 28,413评论 1 232
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 38,397评论 2 309
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 38,099评论 2 314