一、動態(tài)路由簡介
Vue通過路由進行頁面管理,不同的路由綁定到不同的頁面。一般來說,前端直接寫好的路由為靜態(tài)路由,在不修改代碼的情況下,路由表是不會改變的。對于不需要動態(tài)改變路由表的網(wǎng)站,靜態(tài)路由就已經(jīng)足夠了,但是當頁面需要與權限進行綁定時,不同用戶允許瀏覽的頁面是不一樣的,在這種情況下,靜態(tài)路由就難以滿足需求了。動態(tài)路由就是由后端根據(jù)場景生成的合適路由數(shù)據(jù),前端獲取此數(shù)據(jù)并進行解析,最后與固定不變的靜態(tài)路由組合而成的路由。
本文將基于Vite+Vue3+TypeScript構建的Vue項目與mock模擬的后端接口,簡單介紹Vue的動態(tài)路由配置。
此項目代碼已上傳至GitHub,鏈接如下:
https://github.com/XMNHCAS/VueDynamicRoute
二、創(chuàng)建Vite+Vue3+TS基礎項目
首先使用以下命令創(chuàng)建項目
npm create vite 項目名 -- --template vue-ts
在剛剛創(chuàng)建的項目的目錄下執(zhí)行以下命令,安裝vue初始的node依賴包?
npm i
參考示例如下:
項目創(chuàng)建完成后,在項目目錄下執(zhí)行以下命令,使用VSCode打開此項目。也可以直接在此目錄下右鍵點擊使用VSCode打開。
Code .
注意:使用VSCode編輯Vue+TS的項目,建議禁用Vetur,使用Volar插件。
三、項目初始化及靜態(tài)路由配置?
3.1、安裝需要的Node Module?
接下來我們需要安裝幾個node模塊:vue-router、axios和mock。其中vue-router是Vue的路由配置模塊,axios是請求包,mock則是前端模擬api接口的包。
可依次執(zhí)行以下命令,或修改package.json文件之后直接npm i。
安裝vue-router和axios:
npm i vue-router axios -S
安裝mockjs和vite-plugin-mock。由于本項目的mock主要用途僅為模擬后端數(shù)據(jù)接口,所以安裝為開發(fā)依賴,若打包為生產(chǎn)環(huán)境則會失效。
npm i mockjs vite-plugin-mock@2.9.8 -D
ps.?目前vite-plugin-mock最新版本修改了配置選項,而且直接運行可能會報錯,暫時建議使用2.9.8或2.9.8以下的版本,等待作者修復后再使用新版本。若需要使用最新版本,請移步至該項目的Github倉庫(https://github.com/vbenjs/vite-plugin-mock),Issues中有解決報錯的辦法。
3.2、創(chuàng)建需要的文件夾以及文件
首先我們打開src文件夾下的components文件,刪除HelloWorld.vue。打開App.vue,將文件內(nèi)容修改為以下代碼:
<template>
<router-view></router-view>
</template>
然后在src文件夾下分別創(chuàng)建router、utils、apis、views和mock文件夾,并在文件夾中創(chuàng)建如圖所示的文件:
?
3.3、配置vue-router
/src/router/index.ts:此文件為路由配置文件,我們先在此創(chuàng)建初始的靜態(tài)路由。
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
// 靜態(tài)路由表
const routes: Array<RouteRecordRaw> = [
{
// 路由重定向配置
path: '/',
redirect: '/Home'
}, {
path: '/Home',
component: () => import('../views/HomePage.vue')
}
]
// 路由對象
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
3.4、配置mock
/src/mock/index.ts:此文件為mock模擬接口配置,我們先創(chuàng)建一個測試接口,返回一個Hello World
import { MockMethod } from "vite-plugin-mock"
const mock: Array<MockMethod> = [
{
// 接口路徑
url: '/api/test',
// 接口方法
method: 'get',
// 返回數(shù)據(jù)
response: () => {
return {
status: 200,
message: 'success',
data: 'Hello World'
}
}
}
]
export default mock
3.5、配置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/",
}),
]
})
3.6、配置axios
/src/utils/request.ts:此文件為axios配置文件,它將創(chuàng)建一個axios全局單例,由于本項目僅做最簡單的演示,所以僅配置baseUrl,實際使用時可根據(jù)實際情況添加攔截器等功能。
import axios from 'axios'
// axios對象
const service = axios.create({
// axios請求基礎URL
// 由于本項目使用mock + vite-plugin-mock啟動的mock服務,默認使用的端口號與頁面一致
baseURL: "http://localhost:5173",
timeout: 5000
})
export default service
/src/apis/index.ts:此文件為接口文件,接口統(tǒng)一放到此文件中。
import req from '../utils/request'
/**
* 測試接口
*/
// 測試用Hello World
export const TestApi = () => req({ url: '/api/test', method: 'get' })
3.7、首頁代碼
/src/views/HomePage.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>
3.8、配置main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// 啟用路由
app.use(router)
app.mount('#app')
3.9、靜態(tài)路由運行?
使用此命令運行,vite3默認的運行端口為5173。?
npm run dev
運行結果如下所示:
從右側開發(fā)者工具中可以看出,我們模擬的test接口已經(jīng)成功返回了數(shù)據(jù),頁面也正常根據(jù)路由跳轉至Home頁面。
四、配置動態(tài)路由
4.1、配置動態(tài)路由接口
首先我們先在剛剛創(chuàng)建的mock接口文件(/src/mock/index.ts)中添加一個返回路由信息的路由接口,如下所示:
import { MockMethod } from "vite-plugin-mock"
const mock: Array<MockMethod> = [
/**
* 測試接口
*/
{
// 接口路徑
url: '/api/test',
// 接口方法
method: 'get',
// 返回數(shù)據(jù)
response: () => {
return {
status: 200,
message: 'success',
data: 'Hello World'
}
}
},
/**
* 路由數(shù)據(jù)接口
*/
{
url: '/api/routes',
method: 'get',
response: () => {
// 路由
const routes = [
{
path: '/PageOne',
name: 'PageOne',
component: 'PageOne.vue'
}, {
path: '/PageTwo',
name: 'PageTwo',
component: 'PageTwo.vue'
}, {
path: '/PageThree',
name: 'PageThree',
component: 'PageThree.vue',
}
]
return {
status: 200,
message: 'success',
data: routes
}
}
}
]
export default mock
此接口返回三個頁面的路由,根據(jù)這個接口的數(shù)據(jù),我們在views文件夾中創(chuàng)建這三個頁面。?
?
頁面僅由一個h1標簽組成,h1的內(nèi)容為文件名。此外再加上一個按鈕,用以跳轉回Home頁面。PageOne.vue代碼如下:
<template>
<h1>Page One</h1>
<button @click="handleClick">Home</button>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router';
const router = useRouter()
const handleClick = () => router.push({ path: '/Home' })
</script>
其余兩個頁面僅需修改h1標簽的內(nèi)容。
4.2、安裝并配置pinia
我們的動態(tài)路由數(shù)據(jù)應由一個公共的地方進行管理,本文選用vue的狀態(tài)管理器來實現(xiàn)這個功能。pinia是vue新一代的狀態(tài)管理器,與vuex作用基本相同,但是功能比vuex更加強大。
pinia官方文檔:https://pinia.web3doc.top
首先在項目目錄下執(zhí)行以下命令,安裝pinia:
npm i pinia -S
安裝完成后,創(chuàng)建store文件夾,并在此文件夾下創(chuàng)建index.ts文件。
?/src/store/index.ts:
import { defineStore } from 'pinia'
// pinia狀態(tài)管理器
export const useStore = defineStore('myStore', {
state: () => {
return {}
},
getters: {},
actions: {}
})
?完成基礎配置后,需要在mian.ts中引入pinia,需要注意的是,pinia必須在vue-router之后引入。
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')
4.3、添加路由數(shù)據(jù)接口
/src/apis/index.ts:
import req from '../utils/request'
/**
* 測試接口
*/
// 測試用Hello World
export const TestApi = () => req({ url: '/api/test', method: 'get' })
/**
* 動態(tài)路由接口
*/
export const GetDynamicRoutes = () => req({ url: '/api/routes', method: 'get' })
4.4、在pinia中添加配置項
由于我們使用的pinia存儲我們的路由數(shù)據(jù),所以我們需要在pinia的state中添加一個路由項(RouteRecordRaw類型的數(shù)組routes)。
我們在pinia的action中還需要添加一個根據(jù)路由數(shù)據(jù)加載動態(tài)路由的方法(addRoutes),路由數(shù)據(jù)和router對象由外部傳入。外部傳入router是為了避免循環(huán)調(diào)用router,畢竟需要進行加載動態(tài)路由的地方基本都有個router的示例對象,不過也可以直接在此方法里面直接調(diào)用router,并不影響結果,也不會報錯。
加載路由的思路很簡單,首先解析外部傳入的路由數(shù)據(jù),根據(jù)路由的數(shù)據(jù)類型生成對應的路由表,并存儲到pinia中,然后直接遍歷這個pinia中的路由表,使用router.addRoute()方法將路由加載進去。router.addRoute()方法還支持傳如兩個參數(shù),這是為了在指定位置的路由中插入children,這種情況下第一個參數(shù)是父級路由的name,第二個參數(shù)就是要添加的children路由對象。本文的項目并沒有做多層級的路由,所以使用一個參數(shù)即可。
vite使用動態(tài)路由,在動態(tài)導入組件的時候,需要注意不能將頁面路徑直接作為component導入,雖然開發(fā)環(huán)境一般是能正常加載,但是打包到生產(chǎn)環(huán)境的時候就會出錯,所以我們需要添加以下代碼:
let modules = import.meta.glob("../views/Pages/*.vue")
然后使用這個modules來配置組件:
// 錯誤示例:components:()=>import(`../views/Pages/${m.component}`)
// 正確示例如下:
component: modules[`../views/Pages/${m.component}`],
最終的pinia代碼如下:?
?/src/store/index.ts:
import { defineStore } from 'pinia'
import { RouteRecordRaw } from 'vue-router'
let modules = import.meta.glob("../views/Pages/*.vue")
// pinia狀態(tài)管理器
export const useStore = defineStore('myStore', {
state: () => {
return {
// 路由表
routes: [] as Array<RouteRecordRaw>
}
},
getters: {},
actions: {
// 添加動態(tài)路由,并同步到狀態(tài)管理器中
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/Pages/${m.component}`],
})
})
this.routes.forEach(m => router.addRoute(m))
},
}
})
4.5、加載動態(tài)路由
配置了路由接口和加載路由的方法,接下來我們就需要加載我們的動態(tài)路由了。
思路也很簡單,在我們的Home頁面中調(diào)用路由的數(shù)據(jù)接口,在獲取到數(shù)據(jù)之后調(diào)用加載的方法即可。為了驗證我們的路由是否被加載成功,我們還需要添加三個路由對應的按鈕,以便我們進行路由跳轉。
/src/views/HomePage.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, GetDynamicRoutes } from '../apis'
import { useRouter } from 'vue-router'
import { computed } from "@vue/reactivity";
import { onMounted } from "vue";
const router = useRouter()
const store = useStore()
// 動態(tài)路由表
const routes = computed(() => store.routes)
// 路由按鈕點擊事件
const handleClick = (path: string) => {
router.push({ path })
}
onMounted(() => {
if (store.routes.length < 1) {
// 獲取動態(tài)路由
GetDynamicRoutes().then(res => {
store.addRoutes(res.data.data, router)
})
}
// 測試接口
TestApi().then(res => console.log(res.data)).catch(err => console.log(err))
})
</script>
效果如下:?
隨便點擊一個按鈕,我們就能成功跳轉到我們的動態(tài)路由了。
4.6、配置路由守衛(wèi)
到這里,我們的動態(tài)路由已經(jīng)加載成功了。不過其實我們還沒有做完,這個動態(tài)路由還存在一個bug,假如我們刷新跳轉后的頁面,或者直接使用動態(tài)路由的路徑進行跳轉,就會出現(xiàn)以下情況:
可以看到,這種情況下我們的動態(tài)路由失效了,頁面沒了。這是因為我們的路由和pinia在刷新之后都會被重置,而我們加載路由的方法是在Home頁面被調(diào)用的,當我們直接跳轉到動態(tài)加載出來的路徑或者直接在這個路徑刷新的時候,Home頁面并沒有被加載,也就是說我們的動態(tài)路由并沒有被加載上去,自然這個動態(tài)的頁面也就丟失了。
解決方法有很多,舉個例子,既然我們刷新丟了路由是因為Home頁面沒被加載,那我們把加載的方法直接寫到必然會被調(diào)用的App.vue就可以了,這是個簡單直接的方法。不過考慮到實際使用的時候,我們可能需要在路由跳轉時進行鑒權操作,下面介紹另一種方法,使用路由守衛(wèi)進行加載。
其實路由守衛(wèi)加載的思路也很簡單,假如我們的頁面請求路徑不是某個指定的路徑的時候,我們就在跳轉之前先去查詢狀態(tài)管理器中是否存在我們的動態(tài)路由,或者該動態(tài)路由是否滿足我們的跳轉要求,如果不滿足就請求接口并加載我們的動態(tài)路由,并在加載完成后再繼續(xù)跳轉操作。在本項目中,我們的固定頁面是Home頁面,所以只要我們跳轉的不是Home頁面,就查詢pinia是否存在路由表,如果沒有則請求接口獲取路由并加載。代碼示例如下:
/src/router/index.ts:
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
import { useStore } from "../store";
import { GetDynamicRoutes } from '../apis'
// 靜態(tài)路由表
const routes: Array<RouteRecordRaw> = [
{
// 路由重定向配置
path: '/',
redirect: '/Home'
}, {
path: '/Home',
component: () => import('../views/HomePage.vue')
}
]
// 路由對象
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守衛(wèi)
router.beforeEach((to, from, next) => {
if (to.path !== '/Home' && to.path !== '/') {
const store = useStore()
if (store.routes.length < 1) {
GetDynamicRoutes().then(res => {
store.addRoutes(res.data.data, router)
next({ path: to.path, replace: true })
}).catch(_ => {
next()
})
} else {
next()
}
} else {
next()
}
})
export default router
現(xiàn)在我們直接訪問我們的動態(tài)頁面也能成功加載了。
雖然能成功加載了,但是開發(fā)者工具那里還有一條報錯:
[Vue Router warn]: No match found for location with path "/PageOne"
這是因為我們在路由跳轉之前請求了一次接口進行加載,但是在完成路由加載之前,vue-router就直接先去找了這個路徑,我們的路由都還沒加載完成,當然也就找不到了,所以就會出這個警告。其實這個警告完全可以無視,不過我們最好還是處理一下,避免出現(xiàn)意外情況。
解決方法也很簡單,添加一個404頁面就可以了。在views文件夾中添加一個Error文件夾,并在此文件夾中添加404.vue文件。
<template>
<h1>404</h1>
</template>
然后在靜態(tài)路由中添加一個錯誤的路由:
?/src/router/index.ts:
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
import { useStore } from "../store";
import { GetDynamicRoutes } from '../apis'
// 靜態(tài)路由表
const routes: Array<RouteRecordRaw> = [
{
// 路由重定向配置
path: '/',
redirect: '/Home'
}, {
path: '/Home',
component: () => import('../views/HomePage.vue')
}, {
// 404頁面配置
path: '/:catchAll(.*)',
component: () => import('../views/Errors/404.vue')
}
]
// 路由對象
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守衛(wèi)
router.beforeEach((to, from, next) => {
if (to.path !== '/Home' && to.path !== '/') {
const store = useStore()
if (store.routes.length < 1) {
GetDynamicRoutes().then(res => {
store.addRoutes(res.data.data, router)
next({ path: to.path, replace: true })
}).catch(_ => {
next()
})
} else {
next()
}
} else {
next()
}
})
export default router
?效果如下:
文章來源:http://www.zghlxwxcb.cn/news/detail-782769.html
五、結尾
至此我們的動態(tài)路由就完成了,動態(tài)路由的實現(xiàn)方法有很多,本文介紹的也只是其中一種,也有很大的優(yōu)化空間,這個就看需要參照實際需求了。文章來源地址http://www.zghlxwxcb.cn/news/detail-782769.html
到了這里,關于Vue3動態(tài)路由(Vite+Vue3+TS+Mock)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!