vue-router 組件
router-link
-
功能:用于導航,即渲染一個鏈接,當點擊時,導航到由 to 屬性指定的 URL。
-
示例:
<router-link to="/home">Home</router-link>
-
它會渲染為一個
<a>
標簽,但如果你希望它渲染為其他標簽,你可以使用 tag 屬性,如<router-link to="/home" tag="li">Home</router-link>
。
router-link 的一些屬性:
-
to:必填,標識目標路由的鏈接,當被點擊后,內部會立刻把 to 的值傳到 router.push(),所以這個值可以是一個字符串或者描述目標路由的對象
-
repalce:默認值 false,若設置的話,當點擊時,會調用 router.replace() 而不是 router.push()
-
append:設置 append 屬性后,則在當前 (相對) 路徑前添加基路徑。例如,我們從 /a 導航到一個相對路徑 b,如果沒有配置 append,則路徑為 /b,如果配了,則為 /a/b
-
event:聲明可以用來觸發導航的事件,可以是一個字符串或是一個包含字符串的數組,默認是 click
-
exact:是否精確匹配,默認為 false。例如,路徑 /user 精確匹配只會匹配 /user,而不會匹配 /user/123 或 /user/abc。默認匹配就會匹配任何以 /user 開頭的路徑
-
tag:讓
<router-link>
渲染成 tag 設置的標簽,如 tag=“li”,就會渲染成<li>跳轉</li>
-
active-class:默認值為 router-link-acitve,設置鏈接激活時使用的 CSS 類名,默認值可以通過路由的構造選項 linkActiveClass 來全局配置
-
exact-active-class:默認值為 router-link-exact-active,設置鏈接被精確匹配的時候應該激活的 class,默認值可以通過路由構造函數選項 linkExactActiveClass 進行全局配置
router-view
-
功能:組件的出口,即路由匹配到的組件將渲染在這里。
-
它可以理解為是一個占位符,Vue Router 會根據當前路由規則動態地替換顯示相應的組件。
-
通常放在 App 組件的模板中,作為整個應用的布局中心。
vue-router 功能
嵌套路由
Vue Router 嵌套路由指的是在一個路由的基礎上,再嵌套其他子路由,用于構建更復雜的頁面層級結構,讓頁面組件可以進行嵌套展示。
例如點擊頂部導航欄,左邊顯示不同的菜單欄。
<template><div id="app"><h1>初始頁面</h1><router-link to="/home">Home</router-link><!-- 路由出口,根據當前的路由狀態渲染匹配到的組件內容 --><!-- 當點擊上面的鏈接時此處會顯示 Home 組件的內容 --><router-view></router-view></div>
</template><script>
export default {name: 'App'
}
</script>
<!-- Home.vue -->
<template><div><h1>Welcome to Home</h1><router-link to="/about">About</router-link><router-link to="/team">Team</router-link><router-view></router-view> <!-- 嵌套路由的出口 --></div>
</template>
// router/index.js
import Home from '@/components/Home'
import About from '@/components/About'
import Team from '@/components/Team'export default new Router({routes: [{path: '/home',component: Home,children: [{path: '/about',component: About},{path: '/team',component: Team}]}]
})
動態路由參數
Vue Router 支持動態路由參數,如 /user/:id
,其中 :id
是一個動態參數。
帶參數跳轉:
$router.push(`/user/${this.userId}`);
獲取參數:
$route.params.id
路由傳遞數據
有 query 傳參和 params 兩種傳遞數據方式,后面會詳細說明。
供路由守衛
提供路由守衛,允許你在導航過程中執行自定義邏輯來攔截導航并改變其行為。
HTML5 history 和 hash 模式
Vue Router 支持兩種 URL 模式:HTML5 history 模式和 hash 模式。
HTML5 history 模式使用瀏覽器的歷史記錄 API 來管理 URL,而 hash 模式則使用 URL 的 hash 部分來模擬頁面跳轉。
在支持 HTML5 history API 的現代瀏覽器中,推薦使用 HTML5 history 模式以獲得更好的 URL 體驗。
URL 的正確編碼
Vue Router 會自動對 URL 進行正確的編碼和解碼,以確保 URL 的有效性和可讀性。
當你需要傳遞特殊字符或 URL 片段時,Vue Router 會確保這些字符被正確地處理。
過渡效果
可以使用 Vue 的 <transition>
組件來定義過渡效果。
自動激活 CSS 類的鏈接
Vue Router 會自動為當前激活的鏈接添加一個 CSS 類名,這通常用于高亮當前激活的導航鏈接。
你可以通過配置 linkActiveClass 選項來自定義這個類名。
<!--方法一-->
<router-link to='/' active-class="active" >首頁</router-link><!--方法二-->
<script>
const router = new VueRouter({routes,linkActiveClass: 'active'
});
</script>
可定制的滾動行為
Vue Router 允許你定制頁面跳轉時的滾動行為。
你可以通過配置 scrollBehavior 函數來定義滾動行為。
例如切換到新路由時,頁面要滾動到頂部或保持原先的滾動位置。
const router = createRouter({scrollBehavior(to, from, savedPosition) {// 始終滾動到頂部return { top: 0 }},
})
官方示例
vue-router 跳轉方式
聲明式導航
通過內置組件 router-link 跳轉
<router-link to="/home"></router-link>
編程式導航
通過調用 router 實例的方法跳轉
this.$router.push({name: 'myPage',params: {...}
});this.$router.push({path: '/myPage',query: {...}
});this.$router.push('/myPage');this.$router.replace({name: 'myPage',params: {...}
});this.$router.replace({path: '/myPage',query: {...}
});
注意:name 對應 params,path 對應 query。
query 和 params 的區別
-
query 傳參會顯示在 url 地址上,params 傳參不會顯示地址上
-
query 刷新頁面參數不會消失,params 傳參頁面參數會消失,可以考慮本地存儲解決
this.$router.push({path: '/test',query: { id: 123 }
});
/*
url 格式:http://xxx/test?id=123
模板內獲取數據:this.$route.query.id
*/this.$router.push({name: 'test',params: { id: 123 }
});
/*
url 格式:http://xxx/test
模板內獲取數據:this.$route.params.id
*/this.$router.push({path: '/path/${id}'
});
/*
url 格式:http://xxx/test/123
模板內獲取數據:this.$route.params.id
組件路由:/test:id
*/
再次注意:name 對應 params,path 對應 query。
$router.push 和 $router.replace 的區別
-
$router.push:
這個方法會向瀏覽器歷史記錄中添加一個新的記錄。
也就是說,當你調用 $router.push 跳轉到一個新的路由時,用戶點擊瀏覽器的“返回”按鈕時,會返回到之前的頁面。
-
$router.replace:
這個方法會替換當前的歷史記錄,新的路由會替代當前路由的位置。
也就是說,當你調用 $router.replace 跳轉時,瀏覽器歷史記錄不會增加新的一項,而是用新的路由替代當前的路由。如果用戶點擊“返回”按鈕,會跳回到替換前的那個頁面。
vue-router 兩種模式
Hash 模式
URL 形式:
Hash 模式的 URL 包含 # 符號,后面跟著路由路徑。例如:http://example.com/#/home,其中 /home 是路由路徑。
原理:
Hash 模式利用瀏覽器對 URL 中 # 符號的特殊處理。# 后面的部分被視為錨點,通常用于滾動頁面至特定位置。
在 Vue Router 中,當 URL 的 hash 值變化時(無論是通過點擊鏈接、編程式導航,還是手動修改地址欄并按回車),瀏覽器不會發送請求到服務器,而是觸發 hashchange 事件。Vue Router 會監聽該事件,并根據新的 hash 值匹配對應的路由組件進行渲染。
特點:
-
兼容性好: 由于 # 符號被所有現代瀏覽器支持,Hash 模式能在各類瀏覽器上正常工作,且對老舊瀏覽器也有較好的兼容性。
-
部署簡單: Hash 模式不需要服務器特殊配置,因為 # 后的內容不會被發送到服務器。
-
SEO 性能差: 搜索引擎通常忽略 # 后的內容,因此 Hash 模式不利于 SEO。
-
URL 不美觀: URL 中包含 # 符號,可能顯得不夠美觀,尤其對不熟悉 SPA 的用戶來說。
注意:
前面說到當 URL # 后面的內容發生變化時,瀏覽器不會發送請求到服務器。這是因為:在單頁面應用中,所有的頁面內容(HTML、CSS、JavaScript 等)在初始加載時就已經從服務器獲取并加載到瀏覽器內存中了。后續切換路由,實際上是在已經加載的頁面基礎上,通過 JavaScript 操作 DOM 來顯示或隱藏不同的內容區域,實現類似頁面切換的效果,而不是像傳統的多頁面應用那樣,每次頁面切換都要從服務器獲取全新的 HTML 頁面。
History 模式
也叫 HTML5 模式。
URL 形式:
History 模式下的 URL 整潔,無 # 符號,呈現常規路徑結構,如:http://example.com/home。
原理:
利用 HTML5 的 History API(history.pushState()
和 history.replaceState()
)實現 URL 跳轉和狀態管理。
Vue Router 在該模式下,當用戶點擊路由鏈接或通過 JavaScript 代碼調用 router.push (‘/new-path’) 時,會使用 pushState() 或 replaceState() 方法更新瀏覽器歷史記錄和瀏覽器當前 URL。同時,Vue Router 監聽 popstate 事件捕獲瀏覽器的前進后退操作,并依據新 URL 計算對應路由,動態加載組件。
特點:
-
URL 美觀性好:沒有 #,符合傳統網站鏈接的預期。
-
SEO 性能較好:搜索引擎更容易抓取和索引。
-
需要服務器配置:需要配置服務器使得直接訪問 URL 時能夠返回應用的入口頁面(如 index.html)。
-
兼容性問題:現代瀏覽器支持良好,但老版本瀏覽器可能存在兼容性問題。
注意:
當用戶通過 Vue Router 的導航(例如點擊 <router-link>
或調用 router.push()
)切換路由時,URL 會發生變化,但不會向服務器發送請求。Vue Router 會在瀏覽器內存中動態渲染對應的組件,而不會重新加載頁面或從服務器獲取新的內容。
但是當用戶直接在瀏覽器地址欄中輸入一個 URL(例如 http://example.com/home)或者刷新頁面時,瀏覽器會向服務器發送一個請求,要求獲取該 URL 對應的資源。
-
如果服務器沒有正確配置(例如沒有將所有路由請求重定向到 index.html),服務器會嘗試查找 /home 對應的文件或資源。由于這個路徑在服務器上并不存在,服務器會返回 404 錯誤。
-
如果服務器配置正確(例如將所有路由請求重定向到 index.html),服務器會返回 index.html 文件。瀏覽器加載 index.html 后,Vue Router 會根據當前的 URL(例如 /home)在瀏覽器內存中渲染對應的組件。
這與 Hash 模式不同,在 Hash 模式 下,直接在瀏覽器地址欄中輸入 URL 并訪問是不會向服務器發起網絡請求的。所以 Hash 模式不需要將所有路由請求重定向到 index.html。
注意這一章節中的路由都是靜態路由,如果是懶加載路由,情況又會有所不同。
vue-router 懶加載
Vue 路由懶加載是指在用戶訪問某個路由時,才動態加載該路由對應的組件代碼,而不是在應用程序初始化時就加載所有組件。這種按需加載的方式可以有效提高頁面加載速度和應用程序性能。
路由懶加載的核心思想是將路由對應的組件打包成單獨的文件,在需要的時候再進行加載。當用戶首次訪問某個使用了路由懶加載的路由時,由于該組件對應的文件還沒有被加載到瀏覽器內存中,所以需要向服務器發起請求來獲取這個組件的代碼。
懶加載簡單來說就是延遲加載或按需加載,即在需要的時候的時候進行加載。
未用懶加載
import Vue from 'vue'
import Router from 'vue-router'import HelloWorld from '@/components/HelloWorld'Vue.use(Router)export default new Router({routes: [{path:'/',name: 'HelloWorld',component: HelloWorld}]
})
webpack 動態導入實現懶加載
import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)export default new Router({routes: [{path: '/',name: 'HelloWorld',component: resolve=>require(["@/components/HelloWorld"] , resolve)}]
})
ES6 import() 實現懶加載
最常用
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)export default new Router({routes: [{path: '/',name: 'HelloWorld',component: ()=>import("@/components/HelloWorld")}]
})
vue-router 中的導航鉤子
作用:在路由導航發生前后執行特定邏輯,例如權限驗證、完成或取消路由跳轉等。
有三種方式可以植入到路由導航過程中:
-
全局守衛:全局的
-
路由獨享守衛:單個路由獨享的
-
組件內守衛:組件級的
全局守衛
全局前置守衛:beforeEach
作用:在路由切換之前被調用,每次路由切換都會觸發。
參數:
-
to:即將進入的目標路由對象。
-
from:當前導航正要離開的路由對象。
-
next:必須調用該方法來 resolve 這個鉤子。
-
next():允許導航。
-
next(false):中斷當前的導航。
-
next(‘/’) 或 next({ path: ‘/’ }):當前導航被中斷,進行新的一個導航。
-
next(error):導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
-
示例:
const router = new VueRouter({... })
router.beforeEach((to, from, next) => {const isAuthenticated = localStorage.getItem('isAuthenticated');if (to.meta.requiresAuth &&!isAuthenticated) {next('/login');} else {next();}
});
全局解析守衛:beforeResolve
作用:在 beforeEach 之后,beforeEnter 之前,并且在所有組件內守衛和異步路由組件被解析之后調用。主要用于確保在進入路由之前,所有的異步組件都已經解析完成。很少用到。
參數:和 beforeEach 相同,即 to、from、next。
示例:
router.beforeResolve((to, from, next) => {// 可以在這里做一些等待異步組件解析完成的操作next();
});
全局后置鉤子:afterEach
作用:每次路由跳轉結束后調用,它不接受 next 參數,因此不能阻止導航。通常用于統計和日志記錄。
參數:
-
to:成功跳轉的目標路由對象。
-
from:離開的路由對象。
示例:
router.afterEach((to, from) => {// 進行頁面統計或日志記錄console.log(`從 ${from.name} 跳轉到了 ${to.name}`);
});
路由獨享守衛:beforeEnter
beforeEnter(to, from, next):在進入特定路由前執行,配置在路由配置對象中。
參數與 beforeEach 相同。
示例:
const router = new VueRouter({routes: [{path: '/admin',component: AdminComponent,beforeEnter: (to, from, next) => {const isAdmin = localStorage.getItem('isAdmin');if (isAdmin) {next();} else {next('/');}}}]
});
組件內守衛
beforeRouteEnter
作用:在進入該組件的對應路由時調用,此時組件實例還未被創建,所以不能訪問 this。
參數:
-
to:即將要進入的目標路由對象。
-
from:當前導航正要離開的路由對象。
-
next:next 函數可以接收一個回調函數作為參數,該回調函數在組件實例被創建后調用,并且可以將 this 作為參數傳遞給回調函數,以便在組件內進行操作。
注意:只有 beforeRouteEnter 支持給 next 傳遞回調。
示例:
export default {beforeRouteEnter(to, from, next) {next((vm) => {// vm 是組件實例vm.initData();});},methods: {initData() {// 初始化數據的方法}}
};
beforeRouteUpdate
作用:在當前路由改變,但是該組件被復用時調用,例如在一個帶有參數的動態路由中,參數變化時,組件實例不會重新創建,此時會調用這個鉤子。
參數:
-
to:即將要進入的目標路由對象。
-
from:當前導航正要離開的路由對象。
-
next:和其他鉤子中的 next 函數作用相同。
示例:
export default {beforeRouteUpdate(to, from, next) {// 處理參數變化后的邏輯this.fetchData(to.params.id);next();},methods: {fetchData(id) {// 獲取數據的方法}}
};
beforeRouteLeave
作用:在離開該組件的對應路由時調用,可以用于防止用戶未保存數據就離開頁面等場景。
參數:
-
to:即將要進入的目標路由對象。
-
from:當前導航正要離開的路由對象。
-
next:和其他鉤子中的 next 函數作用相同。
示例:
export default {beforeRouteLeave(to, from, next) {const isDataSaved = this.isDataSaved();if (isDataSaved) {next();} else {if (confirm('數據未保存,是否離開?')) {next();} else {next(false);}}},methods: {isDataSaved() {// 判斷數據是否已保存的方法}}
};
導航守衛三個參數的含義
-
to:即將進入的目標路由對象。
-
from:當前導航正要離開的路由對象。
-
next:必須調用該方法來 resolve 這個鉤子。
-
next():允許導航。
-
next(false):中斷當前的導航。
-
next(‘/’) 或 next({ path: ‘/’ }) 或 next(‘/new-path’):當前導航被中斷,進行一個新的導航。
注意:進行一個新的導航并不是說直接進入到了新的導航,實際上是中斷了當前的導航,并重新進入路由守衛。只有遇到了
next()
才是真正的放行。 -
next(error):導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
-
next((vm) => { … }):只有 beforeRouteEnter 鉤子才有。
-
next({ …to, replace: true }):重新導航到當前正要進入的路由,并且使用 replace 模式,替換當前的歷史記錄。
- 例如在用戶登錄成功后,你可能不希望用戶能通過瀏覽器的“返回”按鈕回到登錄頁面。
-
完整的導航解析流程
-
導航被觸發:
用戶進行了如點擊鏈接、調用編程式導航方法(比如在 Vue Router 中使用router.push
等)等操作,從而觸發了一次新的路由導航。 -
在失活的組件里調用離開守衛:
如果當前頁面存在正在顯示的組件(即將要離開的組件),那么會首先調用該組件內的離開守衛(例如在 Vue Router 中組件內的beforeRouteLeave
方法)。這個守衛可以用于詢問用戶是否真的要離開當前頁面,比如有未保存的表單數據時提示用戶。 -
調用全局的 beforeEach 守衛:
接著會調用全局定義的beforeEach
守衛函數。這個守衛在每次路由導航之前都會被調用,它可以用于全局的權限驗證、記錄導航日志等功能。可以接受to
(即將要進入的路由)、from
(當前所在的路由)和next
(用于控制導航流程的函數)作為參數。 -
在重用的組件里調用 beforeRouteUpdate 守衛:
當新的導航目標與當前路由的組件是同一個(即組件被重用,比如只是路由參數發生了變化,而組件本身沒有改變)時,會調用該組件內的beforeRouteUpdate
守衛。這個守衛可以用于在組件重用時根據新的路由參數進行一些數據的更新操作。 -
在路由配置里調用 beforeEnter:
對于即將要進入的路由配置中,如果定義了beforeEnter
守衛函數,此時會調用它。beforeEnter
守衛是針對單個路由配置的,同樣接受to
、from
和next
參數,可以對特定的路由進行單獨的權限或其他邏輯檢查。 -
解析異步路由組件:
如果即將要進入的路由對應的組件是異步加載的(比如通過() => import('./SomeComponent.vue')
這種方式定義的異步組件),此時會開始解析并加載該異步組件。 -
在被激活的組件里調用 beforeRouteEnter:
對于即將要顯示的組件(即將被激活的組件),會調用其內部的beforeRouteEnter
守衛函數。這個守衛在組件實例被創建之前調用,所以無法訪問組件實例this
。它也接受to
、from
和next
參數,并且可以通過next
傳遞一個回調函數,該回調函數會在組件實例創建后被調用。 -
調用全局的 beforeResolve 守衛:
在上述步驟完成后,會調用全局的beforeResolve
守衛。這個守衛在導航被確認之前調用,主要用于確保所有的異步組件都已經解析完成,以及其他需要在導航確認前完成的操作。 -
導航被確認:
經過前面一系列的守衛檢查和組件解析等操作后,如果所有的next
函數都被正確調用并且沒有被阻止(例如next(false)
會阻止導航),此時導航被確認,即將進入新的路由頁面。 -
調用全局的 afterEach 鉤子:
導航確認后,會立即調用全局的afterEach
鉤子函數。這個鉤子函數不接受next
參數,主要用于進行一些不需要控制導航流程的操作,比如頁面切換后的統計分析、更新頁面標題等。 -
觸發 DOM 更新:
在導航確認和調用afterEach
鉤子之后,會根據新的路由渲染對應的組件,從而觸發 DOM 的更新,將新的頁面內容顯示在瀏覽器中。 -
在創建好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數:
如果在步驟 7 中beforeRouteEnter
守衛的next
函數傳遞了一個回調函數,那么在組件實例創建完成并且 DOM 更新之后,會調用這個回調函數。可以在這個回調函數中訪問到已經創建好的組件實例this
,進行一些基于組件實例的初始化操作等。
route 和 router 的區別
-
this.$route 是當前路由信息對象,包括 path、params、hash、query、fullPath、matched、name 等路由信息參數
-
this.$router 是路由實例對象,包括了路由的跳轉方式(push()、replace()、go())、鉤子函數等
如何監聽路由參數的變化
-
watch 監聽 $route 對象
watch: {$route(to, from) {console.log(to, from)} }
-
調用組件內的守衛 beforeRouteUpdate
beforeRouteUpdate(to, from, next) {console.log(to, from)next() }
創建、動態添加、刪除、重置路由
Vue2
// src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';Vue.use(VueRouter);// 靜態路由
const staticRoutes = [{path: '/login',name: 'Login',component: ()=> import(@/views/login/index)},{path: '/',name: 'Home',component: ()=> import(@/views/home/index)},{path: '/404',name: 'NotFound',component: ()=> import(@/views/error-page/index),hidden: true},
];// 創建路由
const createRouter = () => new VueRouter({mode: 'history', // history 模式// mode: 'hash', // hash 模式base: process.env.BASE_URL,routes: staticRoutes
});
const router = createRouter()// 動態路由
const dynamicRoutes = [path: '/about',name: 'About',component: ()=> import(@/views/About/index)
]// 動態添加路由,也可以在其他文件的異步方法中使用
router.addRoutes(dynamicRoutes)// 刪除路由
// Vue2 沒有提供刪除路由的方法,只能通過重置路由的方式來間接實現刪除路由。// 重置路由為初始路由
function resetRouter() {const newRouter = createRouter() // 初始路由router.matcher = newRouter.matcher; // 替換路由匹配器
}
// resetRouter(); // 需要的時候再調用// 導出路由
export default router;
Vue3
官網鏈接
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';// 靜態路由
const staticRoutes = [{path: '/login',name: 'Login',component: () => import('@/views/login/index')},{path: '/',name: 'Home',component: () => import('@/views/home/index')},{path: '/404',name: 'NotFound',component: () => import('@/views/error-page/index'),hidden: true},
];// 創建路由
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL), // history 模式// history: createWebHashHistory(import.meta.env.BASE_URL), // hash 模式routes: staticRoutes
});// 動態路由
const dynamicRoutes = [{path: '/about',name: 'About',component: () => import('@/views/About/index')}
];// 動態添加路由
/*
注意:
Vue2 是 addRoutes,它接受一個路由數組作為參數;
Vue3 是 addRoute,一次添加一個路由。
*/
dynamicRoutes.forEach(route => {router.addRoute(route);
});// 封裝 removeRoutes 方法
const removeRoutes = (routeNames) => {routeNames.forEach(name => {// 刪除路由// Vue3 提供了刪除單個路由的方法,參數是路由名稱router.removeRoute(name);});
};
// removeRoutes(['About', 'Home']); // 移除多個路由,需要的時候再調用// 重置路由功能
function resetRouter() {// 創建一個新的路由實例const newRouter = createRouter({history: createWebHistory(process.env.BASE_URL),routes: staticRoutes // 僅包含靜態路由});// 替換原有的路由實例router.matcher = newRouter.matcher;
}// 也可以通過刪除所有路由來達到重置路由的目的
const removeAllRoutes = () => {// 獲取所有路由名稱const allRouteNames = router.getRoutes().map(route => route.name);// 遍歷所有路由名稱并刪除對應路由allRouteNames.forEach(name => {router.removeRoute(name);});
};// 導出路由
export default router;
完整路由代碼示例
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store/index'
import { getToken, removeToken } from "@/utils/index";Vue.use(Router)export const routes = [{path: '/login',component: () => import("@/views/login/index"),hidden: true},{path: '/404',component: () => import("@/views/error-page/404"),hidden: true}
]const createRouter = () => new Router({mode: "history",scrollBehavior: () => ({ y: 0 }),routes
})
const router = createRouter()
export default router/*
重寫 Router 上的 push 方法,對其使用 catch 捕捉異常
*/
const VueRouterPush = Router.prototype.push
Router.prototype.push = function push(to) {return VueRouterPush.call(this, to).catch(err => err)
}export function resetRouter() {const newRouter = createRouter()router.matcher = newRouter.matcher
}const whiteList = ['/login']router.beforeEach(async (to, from, next) => {console.log("to:",to)console.log("from:",from)const token = getToken()if (token) {if (to.path === '/login') {next({ path: '/' })} else {const routers = store.state.routersif (routers && routers.length) {// 需要添加到tags中的內容const tag = {name: to.name, url: to.path, meta: to.meta}store.commit("addTags", tag)next()} else {try {await store.dispatch("getRoutersByToken", token);next({...to,replace: true})} catch (error) {resetRouter();removeToken()store.commit('removeRouters')next('/login')}}}} else {if (whiteList.indexOf(to.path) !== -1) {next()} else {next("/login")}}
})
源碼地址
源碼解析