Vue登錄后添加動態(tài)路由并跳轉(zhuǎn)的實(shí)踐分享
前言
項(xiàng)目框架:Vue3 + TypeScript
有這樣一個需求,系統(tǒng)默認(rèn)只有最基礎(chǔ)的幾個路由,如登錄、404等,其它路由需要在登錄后動態(tài)添加。系統(tǒng)沒有固定首頁,登錄完成后跳轉(zhuǎn)至動態(tài)菜單的第一個菜單頁。
分析
這一邏輯乍一看很簡單,其實(shí)有很多小坑在里面。其中最容易踩的的坑是動態(tài)路由尚未渲染完成
就已經(jīng)觸發(fā)路由跳轉(zhuǎn)了,這時候肯定是404,因?yàn)槁酚刹⒉淮嬖?;另一個容易踩的坑是路由重復(fù)加載
,此時頁面會顯示空白,需要手動刷新才能正常顯示。
首先想到的就是使用 Promise
函數(shù)解決,結(jié)果行不通。addRoute
是一個宏任務(wù) 和 resolve
是微任務(wù),所以 Promise
結(jié)束的時候并不能代表動態(tài)路由已經(jīng)添加完成。
其次又想到使用 async
函數(shù)來確保獲取到登錄成功結(jié)果的時候,路由已經(jīng)添加完成,結(jié)果一番嘗試后依然行不通。因?yàn)樘砑勇酚傻牟僮鞑皇钱惒降?,沒有返回 Promise
對象,因此這里的 await
是不會產(chǎn)生效果的。(PS:事后使用 Promise.all
解決了這一問題,后面的具體方法上會說。)
最后,想到了一個很笨的解決方法,輪詢。實(shí)驗(yàn)過后,確定可以實(shí)現(xiàn),但就如開頭說的,這會顯得很 low
,雖然它最終解決了問題。
實(shí)踐
登錄的操作都是一樣的,所以單獨(dú)拿出來只寫一遍。表單就不做介紹了,就從點(diǎn)擊登錄表單校驗(yàn)通過后說起。
所有登錄的代碼放到一個頁面會顯得臃腫,所以具體登錄的操作邏輯我把它抽離了出來。在 src/utils
目錄下創(chuàng)建一個 auth.ts
文件。
auth.ts
import { useRouteListStore } from '@/store/router' const routeListStore = useRouteListStore() // 登錄 export async function Login(data: { username: string; password: string; portal: string; corpCode: string }) { const { username, password, portal, corpCode } = data try { // 登錄接口 const res = await getLogin({ username, password, portal, corpCode }) // ... // 這里寫保存用戶信息及 token 的邏輯 // ... // 添加路由操作,寫在 pinia 中,后面會說 await routeListStore.updateRouteList() return res } catch (err) { return err } }
接下來要寫添加路由的具體邏輯。在 src/store
目錄下創(chuàng)建一個 router.ts
文件,添加內(nèi)容如下:(PS:具體文件路徑要結(jié)合具體的項(xiàng)目內(nèi)容,以下路徑及菜單格式僅作為示例)。
根據(jù)處理方式不同,有兩種方案。
方案一:使用 async 函數(shù)
src/store/router.ts
export const useRouteListStore = defineStore('routeList', { state: () => ({ routeList: [], breadcrumb: [], getRouter: true // 是否需要重新加載路由 }), actions: { // 更新菜單并追加路由 async updateRouteList() { const modules = import.meta.glob('../views/**/*.vue') // 此為接口請求獲取的菜單 const list = await getMenus() list.forEach((e) => { e.route = e.path e.component = () => import('@/layout/index.vue') e.redirect = `${e.path}/${e.children[0].path}` e.children.forEach((item) => { item.route = `${e.path}/${item.path}` item.component = modules[`../views${item.component}.vue`] }) }) await addRouteList(list) this.getRouter = false this.routeList = list return true }, } })
接下來寫動態(tài)添加路由的邏輯,使用 Promise.all 來確保 Pinia 中返回結(jié)果時,動態(tài)路由已經(jīng)加載完成。在 src/router
創(chuàng)建 index.ts
文件,添加內(nèi)容如下:
src/router/index.ts
export function addRouteList(data: any[] = []) { return new Promise((resolve) => { const promises = [] data.forEach((e) => promises.push(router.addRoute(e))) Promise.all(promises).then(() => resolve(true)) }) }
使用 async 函數(shù)之后,登錄頁的操作將會變得很簡單。
login.vue
import { Login } from '@/utils/auth' const onSubmit = () => { validate().then(() => { Login(formState).then(() => { router.push(routerStore.routeList[0].path) }).catch(err => { message.error(err.message) }) }) }
方案二:使用輪詢
輪詢的方案相比于使用 async
函數(shù)要簡單很多,因?yàn)樗恍枰_保登錄后拿到結(jié)果的那一刻,路由是加載完成的。具體實(shí)現(xiàn)代碼如下:
src/store/router.ts
export const useRouteListStore = defineStore('routeList', { state: () => ({ routeList: [], breadcrumb: [], getRouter: true }), actions: { // 更新菜單并追加路由 updateRouteList() { listMenus().then((res) => { const list = res.data if (list === null) { this.getRouter = false router.push('/404') return } list.forEach((e) => { e.route = e.path e.component = () => import('@/layout/index.vue') e.children.forEach((item) => { item.route = `${e.path}/${item.path}` item.component = modules[`../views${item.component}.vue`] }) }) addRouteList(list) this.getRouter = false this.routeList = list }) } })
src/router/index.ts
export function addRouteList(data: any[] = []) { data.forEach((e) => { router.addRoute(e) }) }
輪詢的好處是邏輯簡單,唯一麻煩的一點(diǎn)就是在登錄后添加一個定時器去定期獲取路由是否加載完成。之所以要加定時器是因?yàn)楂@取菜單是異步請求,而程序執(zhí)行時很快的,所以要確保執(zhí)行路由跳轉(zhuǎn)命令時菜單是加載完成的。
login.vue
import { ref, onBeforeUnmount } from 'vue' import { useRouter } from 'vue-router' import { useRouteListStore } from '@/store/router' const routerStore = useRouteListStore() import { Login } from '@/utils/auth' const router = useRouter() // 每0.5s判斷一次菜單是否加載完成,最多判斷30次,超過則說明網(wǎng)絡(luò)環(huán)境極差 const timer = ref(null) const onSubmit = () => { validate().then(() => { Login(formState).then(() => { let i = 0 timer.value = setInterval(() => { if (routerStore.routeList[0].path) { router.push(routerStore.routeList[0].path) } i++ if (i > 30) { clearInterval(timer.value) timer.value = null i = null message.error('當(dāng)前網(wǎng)絡(luò)環(huán)境較差!') spinning.value = false } }, 500) }) }) } // 不要忘記清除定時器 onBeforeUnmount(() => { clearInterval(timer.value) timer.value = null })
補(bǔ)充
以上代碼只能保證系統(tǒng)初次登錄后可以正常跳轉(zhuǎn)頁面,如果退出當(dāng)前賬號,重新登錄或者更換賬號登錄,會出現(xiàn)路由重復(fù)加載的問題,也就是文章開頭所說的另一個容易踩的坑。這個坑解決起來并不困難,只要注意到了,很容易就可以解決。
解決思路是添加路由前置守衛(wèi),同時在 Pinia 中添加一個字段判斷當(dāng)前路由是否需要重新加載即可。具體代碼如下:
import Cookies from 'js-cookie' import { useRouteListStore } from '@/store/router' // 前置守衛(wèi) router.beforeEach(async (to, from, next) => { const token = Cookies.get('token') if (!token) { next({ path: '/login' }) } else { const routerStore = useRouteListStore() routerStore.addBreadcrumb(to) // 判斷菜單是否存在且是否需要重新加載 if (routerStore.routeList.length === 0 && routerStore.getRouter) { await routerStore.updateRouteList() next({ path: to.path, query: to.query }) } else { next() } } })
如對本文有疑問或不同看法,歡迎在評論區(qū)指出。
以上就是Vue登錄后添加動態(tài)路由并跳轉(zhuǎn)的實(shí)踐分享的詳細(xì)內(nèi)容,更多關(guān)于Vue添加動態(tài)路由并跳轉(zhuǎn)的資料請關(guān)注腳本之家其它相關(guān)文章!
- vue?+elementui?項(xiàng)目登錄通過不同賬號切換側(cè)邊欄菜單的顏色
- 登錄頁面的實(shí)現(xiàn)及跳轉(zhuǎn)代碼實(shí)例(vue-router)
- VUE登錄注冊頁面完整代碼(直接復(fù)制)
- vue前端實(shí)現(xiàn)驗(yàn)證碼登錄功能
- vue實(shí)現(xiàn)登錄數(shù)據(jù)的持久化的使用示例
- Vue實(shí)現(xiàn)驗(yàn)證碼登錄的超詳細(xì)步驟
- 通用vue組件化登錄頁面實(shí)例代碼
- vue實(shí)現(xiàn)未登錄訪問其他頁面自動跳轉(zhuǎn)登錄頁功能(實(shí)現(xiàn)步驟)
- Vue精美簡潔登錄頁完整代碼實(shí)例
- vue限制實(shí)現(xiàn)不登錄無法進(jìn)入其他頁面
相關(guān)文章
vue自適應(yīng)布局postcss-px2rem詳解
這篇文章主要介紹了vue自適應(yīng)布局(postcss-px2rem)的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-05-05vue3+ts+EsLint+Prettier規(guī)范代碼的方法實(shí)現(xiàn)
本文主要介紹在Vue3中使用TypeScript做開發(fā)時,如何安裝與配置EsLint和Prettier,以提高編碼規(guī)范。感興趣的可以了解一下2021-10-10vue 中 get / delete 傳遞數(shù)組參數(shù)方法
這篇文章主要介紹了vue 中 get / delete 傳遞數(shù)組參數(shù)方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03vue3.x使用swiper實(shí)現(xiàn)卡片輪播
這篇文章主要為大家詳細(xì)介紹了vue3.x使用swiper實(shí)現(xiàn)卡片輪播,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07Monaco-editor 的 JSON Schema 配置及使用介紹
這篇文章主要為大家介紹了Monaco-editor 的 JSON Schema 配置及使用介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10antd vue表格可編輯單元格以及求和實(shí)現(xiàn)方式
這篇文章主要介紹了antd vue表格可編輯單元格以及求和實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04利用Vue與D3.js創(chuàng)建交互式數(shù)據(jù)可視化
在現(xiàn)代Web開發(fā)中,數(shù)據(jù)可視化是一個引人矚目的熱點(diǎn)領(lǐng)域,從簡單的圖表到復(fù)雜的交互式儀表盤,數(shù)據(jù)可視化能夠幫助用戶更好地理解數(shù)據(jù),而Vue與D3.js的結(jié)合則為我們提供了構(gòu)建這些可視化效果的強(qiáng)大工具,本文將向您展示如何利用Vue與D3.js創(chuàng)建一個基本的交互式數(shù)據(jù)可視化項(xiàng)目2025-02-02