Vue 系列之:路由
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.routers
if (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")
}
}
})
源码地址
源码解析