最近專案中使用了vue-router的addRoutes這個api,遇到了一個小坑,記錄總結一下。
場景復現:
做前端開發的同學,大多都遇到過這種需求:頁面選單根據使用者許可權動態生成,一個常見的解決方案是:
前端初始化的時候,只掛載不需要許可權路由,如登陸,註冊等頁面路由,然後等使用者登入之後,後端返回當前使用者的許可權表,前端根據這個許可權表遍歷前端路由表,動態生成使用者許可權路由,然後使用vue-router提供的addRoutes,將許可權路由表動態新增到路由例項中,整個過程大致如下:
// router.js 檔案
// 需要使用者許可權的路由表
const appRoutes = [
{
path: '/dashboard',
name: 'dashboard',
component: () => import('...'),
children: [
RouteConfig1,
RouteConfig2,
...
]
},
RouteConfig,
...
];
// 不需要使用者許可權的路由表
const constantRoutes = [
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register
},
...
]
// 初始化路由的時候,只掛載不需要使用者許可權的路由表
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
constantRoutes
});
/**
*
* 假如後端返回的資料格式如下:
*
* {
* status: 200,
* message: 'successful',
* data: {
* user: {...},
* token: '...',
* permisssion: [...]
* }
* }
*
* login.vue
*/
axios.post('/user/login',{username,password})
.then(res => {
if (res.status === 200) {
// 如果登入成功,則需要遍歷生成使用者許可權路由
// filterRoutes根據permission和router.js中定義的appRoutes生成動態路由表
const routes = filterRoutes(permission);
// 然後使用addRoutes將routes掛載到router中
router.addRoutes(routes);
} else {
...
}
})
.catch(error => { ... })
複製程式碼
寫到這裡,貌似動態生成路由的功能就好了,一切都perfect了,但問題緊接著就來了,當使用者登入之後,我們點選頁面上的退出按鈕退出當前登入,然後重新登入,會發現瀏覽器console皮膚緊接著就報如下錯誤:
納尼(⊙o⊙)?這是怎麼回事呢,第二次登入也正常登入了,功能上似乎沒有什麼問題,但這個警告從哪裡來的呢?對於一個重度強迫症患者來說,任何警告和報錯都是不允許出現的,哪怕功能上沒什麼問題。
捋一捋
這段警告的意思是說,以上的這幾個路由命名重複,存在多個name相同的路由。那麼為什麼會有多個路由名稱相同的路由呢?
讓我們從頭捋一下這個錯誤是怎麼來的。首先第一次開啟網站登入的時候是沒有問題的,只有當我們退出登入,重新登入的時候,這段警告就來了。並且如果我們在重複登入之前重新整理一下瀏覽器然後再登入,這種警告就不會出現了,很神奇是不是?
分析一下上面的情景:首先這個警告只會在使用者重新登入的時候出現,登入的時候我們做的唯一跟路由相關的事情就是動態新增路由,所以問題肯定出在 router.addRoutes(routes)
這裡,其次這裡又分了兩種情況:有重新整理和無重新整理。在無重新整理的情況下會報這個警告,有重新整理就不會報這個警告。那麼有重新整理和無重新整理有什麼區別呢?
我們很容易就想到,當頁面重新整理的時候,Vue例項會重新初始化,Vue例項初始化的過程中,掛載在它上面的Vue-Router,Store等內容也會重新初始化。而在不重新整理的情況下,就不會重新初始化。
再想想,我們第一次登入之後,通過addRoutes新增了許可權路由routes到router上,假設我們這個許可權routes中包括了dashboard,user,role三個路由,那麼當我們退出登入,然後重新登入的時候,由於同一個使用者登入,後端返回的許可權列表是一樣的,生成的動態路由routes也是一樣的(即裡面同樣包含了dashboard,user,role三個路由),那麼此時再次新增這三個路由就導致router中掛載的routes重複。而在重新整理的情況下,由於router重新初始化,只包含了初始化我們新增的不需要許可權的路由,此時再次登入,重新新增就不存在路由重複的問題了。
通過以上的分析,我們搞清了問題的來源,那麼如何解決呢,很遺憾,vue-router並沒有刪除路由的api。根據以上的分析,我們很容易想到,通過強制重新整理頁面的方式來重置router:即當使用者退出登入的時候,通過js強制重新整理一下頁面。就可以解決問題。這種方式雖然可以解決問題,但顯得不是很優雅,而且重新整理頁面導致資源重新載入和頁面閃爍,體驗也不是特別好。因此有沒有在不重新整理的情況下解決問題的辦法呢?
經過一番搜尋,終於找到了一種方法,即重置當前router的match屬性:
router.js
// 定義一個函式來建立router
export const createRouter = routes => new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
// 在使用addRoutes的地方
// 重置當前router的match = 初始router.match
router.match = createRouter(constantRoutes).match;
router.addRoutes(routes);
複製程式碼
這樣就可以完美解決問題了。
總結:
整個解決的過程還是比較痛苦的,因為實際中我的程式碼是比較複雜的,並不像上面簡化後那麼簡單。整個addRoutes是在store.dispatch中完成,並且中間還夾雜著生成動態路由,根據動態路由再生成使用者選單等一系列功能,干擾比較大,並且這個是原始碼報警,不好定位,只能通過console和瀏覽器除錯,一步步縮小報錯範圍,最終找到問題原因。然後再通過google,以及搜尋vue-router倉庫的issue一步步找到解決方法。所以想說,如果大家開發中遇到一些第三方依賴的問題,可以去搜尋官方倉庫的issue,很好用的,很多問題其實issue中都有答案。我是屢試不爽。最後,一定要用google,垃圾百度,浪費我好長時間,啥都沒找到