vue-router之addRoutes使用遇到的坑

luichooy發表於2019-04-05

最近專案中使用了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皮膚緊接著就報如下錯誤:

menu.saveimg.savepath20190405151956.jpg

納尼(⊙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,垃圾百度,浪費我好長時間,啥都沒找到

參考:github.com/PanJiaChen/…    github.com/vuejs/vue-r…

相關文章