Vue Router 是 Vue 官方的客户端路由解决方案,在单页应用 (SPA) 中,用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。
核心思想:
通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。
官网
https://router.vuejs.org/zh/guide/
安装
通常创建 vue3 项目时,选择安装 Pinia 就会自动集成。
但若目前项目里没有,则按如下流程操作
npm install vue-router@4
新建文件 src/views/Index.vue
<template>
<div>首页</div>
</template>
新建文件 src/router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import Index from "../views/Index.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
component: Index,
},
],
});
export default router;
修改 src/App.vue 为
<template>
<RouterView />
</template>
在 src/main.ts 中导入注册 Vue Router
import router from './router'
app.use(router)
内置组件
RouterLink
用于替代 <a>
标签,创建页面跳转的链接,实现在不重新加载页面的情况下改变 URL。
<RouterLink to="/">首页</RouterLink>
-
to 属性的值为 Vue Router 路由配置中的目标 URL(类似
<a>
标签的 href 属性 ) -
to 属性的值可以是对象,语法和下文中的 push 相同(本质即自动调用 router.push 来触发导航)
-
replace 属性实现下文中 replace 的效果
<router-link :to="..." replace>
-
RouterLink 的 v-slot 中可以访问与下文 useLink 组合式函数相同的属性
自定义 RouterLink
例如导航菜单中的链接,处理外部链接,添加 inactive-class 等
<script setup>
import { computed } from 'vue'
import { RouterLink } from 'vue-router'
defineOptions({
inheritAttrs: false,
})
const props = defineProps({
// 如果使用 TypeScript,请添加 @ts-ignore
...RouterLink.props,
inactiveClass: String,
})
const isExternalLink = computed(() => {
return typeof props.to === 'string' && props.to.startsWith('http')
})
</script>
<template>
<a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
<slot />
</a>
<router-link
v-else
v-bind="$props"
custom
v-slot="{ isActive, href, navigate }"
>
<a
v-bind="$attrs"
:href="href"
@click="navigate"
:class="isActive ? activeClass : inactiveClass"
>
<slot />
</a>
</router-link>
</template>
RouterView
用于渲染当前 URL 路径对应的路由组件,可以放在任何地方,不一定要在 App.vue 中。
<main>
<RouterView />
</main>
命名视图
- name 属性可对视图进行命名(默认值为 default),用于在一个页面,渲染多个组件。
<router-view class="view left-sidebar" name="LeftSidebar" />
<router-view class="view main-content" />
<router-view class="view right-sidebar" name="RightSidebar" />
路由配置时,需配置多个组件,使用 components
属性,值为对象(属性为视图名称,属性值为组件)
{
path: '/',
components: {
default: Home,
// LeftSidebar: LeftSidebar 的缩写
LeftSidebar,
// 它们与 `<router-view>` 上的 `name` 属性匹配
RightSidebar,
},
},
也可在嵌套路由中使用
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
插槽
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
等价于不带插槽的 <router-view />
用途1:添加组件缓存和路由切换的过渡
<router-view v-slot="{ Component }">
<transition>
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
用途2:添加模板引用
<router-view v-slot="{ Component }">
<component :is="Component" ref="mainContent" />
</router-view>
避免将引用放在 <router-view>
上,被 RouterView 的实例填充
用途3:插槽传参【不推荐】
<RouterView v-slot="{ Component }">
<component
:is="Component"
view-prop="value"
/>
</RouterView>
所有视图组件都会接收到 view-prop,即所有的视图组件都声明了一个 view-prop 的 prop,但这未必需要。
路由懒加载
即当路由被访问时才加载对应组件,可以提升性能。
以下写法为静态导入组件
src/router/index.ts 中
import Index from "../views/Index.vue";
{
path: "/",
component: Index,
},
需改为动态导入
采用箭头函数的写法,返回 import() 函数
{
path: "/",
component: () => import("../views/Index.vue"),
},
建议:所有的路由都使用动态导入
配置路由
可通过下方链接,测试路由匹配规则
https://paths.esm.dev/
默认匹配模式
- 不区分大小写
- 匹配带有或不带有尾部斜线的路由
/users 将匹配 /users、/users/、甚至 /Users/
路由模式 history
【推荐】HTML5 模式
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
Hash 模式
会在实际 URL 之前添加哈希字符 #
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
Memory 模式
用于Node 环境和 SSR,不会有历史记录(无法后退或前进)。
import { createRouter, createMemoryHistory } from 'vue-router'
const router = createRouter({
history: createMemoryHistory(),
routes: [
//...
],
})
路由选项 strict sensitive
在 src/router/index.ts 中添加选项
// 严格模式:不匹配带有尾部斜线的路由
strict: true,
// 区分大小写
sensitive: true,
静态路由
像首页这种,路径和组件一一对应的关系为静态路由
{
path: "/",
component: () => import("../views/Index.vue"),
},
动态路由 :
若不同路径,对应的同一个组件,则是动态路由
使用场景
路径参数不同,但渲染同一个组件
实战范例
访问路由 ‘/user/1’ 和 ‘/user/2’ ,都渲染 User 组件,其中的路径参数 1
和 2
为用户 ID
{
path: "/users/:id",
component: () => import("../views/User.vue"),
},
一个路由中可设置多个路径参数
path: "/users/:username/posts/:postId",
页面模板中,可通过 $route.params
获取到路径参数,效果如下
匹配模式 | 匹配路径 | route.params |
---|---|---|
/users/:username | /users/eduardo | { username: 'eduardo' } |
/users/:username/posts/:postId | /users/eduardo/posts/123 | { username: 'eduardo', postId: '123' } |
动态路由切换时,会复用相同的组件实例(提升性能),所以组件的生命周期钩子不会被调用,此时要想对路径参数的变化做出响应,需使用 watch
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
watch(() => route.params.id, (newId, oldId) => {
// 对路由变化做出响应...
})
</script>
或 路由守卫 beforeRouteUpdate
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteUpdate(async (to, from) => {
// 对路由变化做出响应...
userData.value = await fetchUser(to.params.id)
})
</script>
自定义正则的路径参数
可选的路径参数,添加 ?
// 匹配 /users 和 /users/posva
{ path: '/users/:userId?' },
限数字的路径参数,用 (\\d+)
// orderId 只能为数字
{ path: '/:orderId(\\d+)' },
可重复的参数,用 *
(0 个或多个)和 +
(1 个或多个)
// /:chapters -> 匹配 /one, /one/two, /one/two/three, 等
{ path: '/:chapters+' },
// /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
{ path: '/:chapters*' },
结合使用 – 可重复的数字参数
// 匹配 /1, /1/2, 等
{ path: '/:chapters(\\d+)+' },
命名路由 name
可以给路由取个名字,但所有路由的命名都必须是唯一的(路由重名时,只会保留最后一条)
{
path: '/user/:username',
name: 'profile',
component: User
}
可用于路由跳转
<router-link :to="{ name: 'profile', params: { username: 'erina' } }">
User profile
</router-link>
使用 name 还有以下优点:
- 没有硬编码的 URL。
- params 的自动编码/解码。
- 防止你在 URL 中出现打字错误。
- 绕过路径排序,例如展示一个匹配相同路径但排序较低的路由。
嵌套路由 children
页面中的局部区域需要随路由改变时,则需要嵌套路由
实战范例
/user/:id/info
路由显示用户的信息
/user/:id/works
路由显示用户的作品
首先,需将 User 组件局部改变的区域替换为 <router-view />
<!-- User.vue -->
<template>
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<!-- 局部改变的区域 -->
<router-view />
</div>
</template>
嵌套路由的配置如下:
{
path: '/user/:id',
component: User,
children: [
// 当 /user/:id 匹配成功
// UserHome 将被渲染到 User 的 <router-view> 内部
{ path: '', component: UserHome },
{
// 当 /user/:id/info 匹配成功
// UserInfo 将被渲染到 User 的 <router-view> 内部
path: 'info',
component: UserInfo,
},
{
// 当 /user/:id/works 匹配成功
// UserWorks将被渲染到 User 的 <router-view> 内部
path: 'works',
component: UserWorks,
},
组件,其中的路径参数 1
和 2
为用户 ID
路由重定向 redirect
指当用户访问 /home 时,URL 会被 / 替换,然后匹配成 /
// 重定向至 -- 目标路径
[{ path: '/home', redirect: '/' }]
// 重定向至 -- 命名路由
[{ path: '/home', redirect: { name: 'homepage' } }]
// 属性值可以是方法,以便获取路由参数
{
// /search/screens -> /search?q=screens
path: '/search/:searchText',
redirect: to => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
return { path: '/search', query: { q: to.params.searchText } }
},
},
// 相对重定向
{
// 将总是把/users/123/posts重定向到/users/123/profile。
path: '/users/:id/posts',
redirect: to => {
// 该函数接收目标路由作为参数
// 相对位置不以`/`开头
// 或 { path: 'profile'}
return 'profile'
},
},
路由别名 alias
将 / 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /。
{ path: '/', component: Homepage, alias: '/home' }
alias 的值可为数组
{
path: '/users',
component: UsersLayout,
children: [
// 为这 3 个 URL 呈现 UserList
// - /users
// - /users/list
// - /people
{ path: '', component: UserList, alias: ['/people', 'list'] },
],
},
若路由有参数,在任何绝对别名中需包含它们:
{
path: '/users/:id',
component: UsersByIdLayout,
children: [
// 为这 3 个 URL 呈现 UserDetails
// - /users/24
// - /users/24/profile
// - /24
{ path: 'profile', component: UserDetails, alias: ['/:id', ''] },
],
},
路由元信息 meta
在路由上添加更多信息,如过渡名称、访问权限等
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true },
},
{
path: ':id',
component: PostsDetail
// 任何人都可以阅读文章
meta: { requiresAuth: false },
}
]
}
在路由守卫(详见下文)中使用
router.beforeEach((to, from) => {
// 而不是去检查每条路由记录
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: '/login',
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath },
}
}
})
404 路由
写在路由配置的末尾,用于匹配所有路由,展示路由匹配失败的 404 页面。
{ path: '/:pathMatch(.*)*', name: '404', component: () => import("../views/404.vue"), },
路径参数变组件传参 prop
即通过 defineProps 获取路径参数,提升了组件的通用性
<!-- User.vue -->
<script setup>
defineProps({
id: String
})
</script>
<template>
<div>
User {{ id }}
</div>
</template>
路由配置需添加 props 选项
{ path: '/user/:id', component: User, props: true }
命名视图的路由,需为每个命名视图定义 props 配置
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
props 的值可以是函数(需为无状态的)
// “/search?q=vue” 将传递 {query: 'vue'} 作为 props 传给 SearchUser 组件。
{
path: '/search',
component: SearchUser,
props: route => ({ query: route.query.q })
}
路由器对象 $router
模板中为 $router
JS 中为
import { useRouter} from 'vue-router'
const router = useRouter()
- 检查路由是否存在 router.hasRoute()
- 获取所有路由记录的数组 router.getRoutes()
添加路由
在项目运行过程中,添加未注册的路由
router.addRoute({ path: '/about',name:'about', component: About })
为避免名字冲突,可以在路由中使用 Symbol 作为名字。
添加嵌套路由
router.addRoute({
name: 'admin',
path: '/admin',
component: Admin,
children: [{ path: 'settings', component: AdminSettings }],
})
或
router.addRoute({
name: 'admin',
path: '/admin',
component: Admin,
children: [{ path: 'settings', component: AdminSettings }],
})
删除路由
在项目运行过程中,删除已注册的路由,所有的别名和子路由也会被同时删除
方法1 添加重名路由
如果添加与现有途径名称相同的途径,会先删除路由,再添加路由。
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })
方法2 调用添加路由的回调
适合没有名称的路由
const removeRoute = router.addRoute(routeRecord)
removeRoute() // 删除路由如果存在的话
方法3 removeRoute
router.removeRoute(‘home’)
- 参数为路由的名称
当前路由对象 $route
模板中为 $route
JS 中为
import { useRoute } from 'vue-router'
const route = useRoute()
获取当前路由中的参数
route.params
useLink
Vue Router 暴露的路由信息,可用于构建自己的 RouterLink 组件或生成自定义链接,详细用法如下:
<script setup>
import { RouterLink, useLink } from 'vue-router'
import { computed } from 'vue'
const props = defineProps({
// 如果使用 TypeScript,请添加 @ts-ignore
...RouterLink.props,
inactiveClass: String,
})
const {
// 解析出来的路由对象
route,
// 用在链接里的 href
href,
// 布尔类型的 ref 标识链接是否匹配当前路由
isActive,
// 布尔类型的 ref 标识链接是否严格匹配当前路由
isExactActive,
// 导航至该链接的函数
navigate
} = useLink(props)
const isExternalLink = computed(
() => typeof props.to === 'string' && props.to.startsWith('http')
)
</script>
路由跳转
- 模板中跳转路由,通过内置组件 RouterLink
- JS 中跳转路由,通过路由器实例的各种 API
导航是异步的,返回一个 Promise!
首先获取路由器实例
import { useRouter} from 'vue-router'
const router = useRouter()
push 新增跳转
- 会向 history 栈添加一个新的记录,当用户点击浏览器后退按钮时,会回到之前的 URL。( 效果同 window.history.pushState )
- RouterLink 中 to 属性语法与 push 相同
params
不能与path
一起使用- 任何类型的 params 都会转为字符串
// 字符串路径
router.push('/users/eduardo')
// 带有路径的对象
router.push({ path: '/users/eduardo' })
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })
// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })
// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
replace 替代跳转
- 不会向 history 添加新记录( 效果同 window.history.replaceState ),其他与 push 相同
- push 添加 replace 参数,效果与 replace 相同
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
go 越级跳转
参数为整数,用于在历史堆栈中前进或后退多少步( 效果同 window.history.go)
// 向前移动一条记录,与 router.forward() 相同
router.go(1)
// 返回一条记录,与 router.back() 相同
router.go(-1)
// 前进 3 条记录
router.go(3)
// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)
路由守卫
即在路由跳转/取消时自定义逻辑。
全局前置守卫 beforeEach
导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
src/router/index.ts 中
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
参数
- to —— 即将要进入的目标
- from —— 当前导航正要离开的路由
返回值
- false —— 取消当前的导航。
- 一个路由地址 —— 重定向到一个不同的地址
- undefined / true —— 执行导航/调用下一个导航守卫
router.beforeEach(async (to, from) => {
if (
// 检查用户是否已登录
!isAuthenticated &&
// ❗️ 避免无限重定向
to.name !== 'Login'
) {
// 将用户重定向到登录页面
return { name: 'Login' }
}
})
全局解析守卫 beforeResolve
在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用。
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 处理错误,然后取消导航
return false
} else {
// 意料之外的错误,取消导航并把错误传给全局处理器
throw error
}
}
}
})
全局后置钩子 afterEach
用于分析、更改页面标题、声明页面等辅助功能
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
第三个参数为导航结果,值为 true/false
router.afterEach((to, from, failure) => {
if (!failure) sendToAnalytics(to.fullPath)
})
路由守卫内的全局注入
vue3.3
在 app.provide() 中提供的所有内容都可以在 router.beforeEach()、router.beforeResolve()、router.afterEach() 内获取到
// main.ts
const app = createApp(App)
app.provide('global', 'hello injections')
// router.ts or main.ts
router.beforeEach((to, from) => {
const global = inject('global') // 'hello injections'
// a pinia store
const userStore = useAuthStore()
// ...
})
路由配置中定义路由独享守卫
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
beforeEnter 守卫只在进入路由时触发,不会在 params、query 或 hash 改变时触发。
beforeEnter 属性值可以是一个函数数组,便于不同的路由重用守卫
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
组件中定义路由独享守卫
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
// 路由发生变化时触发(仅动态路由参数变化,不会触发)
onBeforeRouteLeave((to, from) => {
const answer = window.confirm(
'Do you really want to leave? you have unsaved changes!'
)
// 取消导航并停留在同一页面上
if (!answer) return false
})
const userData = ref()
// 路由参数变化时触发
onBeforeRouteUpdate(async (to, from) => {
//仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
})
</script>
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫(2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由过渡 transition
全局过渡
所有路由切换都添加过渡效果
<router-view v-slot="{ Component }">
<transition name="fade">
<component :is="Component" />
</transition>
</router-view>
根据路由元信息添加过渡
{
path: '/custom-transition',
component: PanelLeft,
meta: { transition: 'slide-left' },
},
{
path: '/other-transition',
component: PanelRight,
meta: { transition: 'slide-right' },
},
<router-view v-slot="{ Component, route }">
<!-- 使用任何自定义过渡和回退到 `fade` -->
<transition :name="route.meta.transition || 'fade'">
<component :is="Component" />
</transition>
</router-view>
根据路由添加过渡
<!-- 使用动态过渡名称 -->
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition">
<component :is="Component" />
</transition>
</router-view>
根据路径的深度动态添加信息到 meta 字段
router.afterEach((to, from) => {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})
强制过渡 key
Vue 可能会自动复用看起来相似的组件,从而忽略了任何过渡,可通过 key 强制过渡
<router-view v-slot="{ Component, route }">
<transition name="fade">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
路由滚动 scrollBehavior
用于自定义路由切换时页面如何滚动
- 只在支持 history.pushState 的浏览器中可用
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
scrollBehavior 函数
- 参数 to —— 新路由
- 参数 from —— 原路由
- 参数 savedPosition —— 仅 popstate 导航时才可用(由浏览器的后退/前进按钮触发)
- 返回值 —— ScrollToOptions 位置对象,若返回 falsy 值,或者空对象,则不滚动。
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 }
},
相对目标元素偏移
scrollBehavior(to, from, savedPosition) {
// 始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById('main'),
el: '#main',
// 在元素上 10 像素
top: 10,
}
},
若返回 savedPosition,则按下 后退/前进 按钮时,就会像浏览器的原生表现那样
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
},
滚动到锚点 to.hash
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
}
}
},
流畅滚动 smooth
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth',
}
}
}
延迟滚动
通过返回一个 Promise实现,比如希望等待过渡结束后再滚动
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ left: 0, top: 0 })
}, 500)
})
},
})
路由检测
以下情况会路由故障(留在原页面上):
- 用户已经位于他们正在尝试导航到的页面
- 一个导航守卫通过调用 return false 中断了这次导航
- 当前的导航守卫还没有完成时,一个新的导航守卫会出现了
- 一个导航守卫通过返回一个新的位置,重定向到其他地方 (例如,return ‘/login’)
- 一个导航守卫抛出了一个 Error
NavigationFailureType 可获知故障类型
import { NavigationFailureType} from 'vue-router'
- aborted:在导航守卫中返回 false 中断了本次导航。
- cancelled: 在当前导航完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 router.push。
- duplicated:导航被阻止,因为我们已经在目标位置了。
当次路由故障检测
const navigationResult = await router.push('/my-profile')
if (navigationResult) {
// 导航被阻止
} else {
// 导航成功 (包括重新导航的情况)
this.isMenuOpen = false
}
跳转路由前,提醒页面未保存
import { NavigationFailureType, isNavigationFailure } from 'vue-router'
// 试图离开未保存的编辑文本界面
const failure = await router.push('/articles/2')
if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
// 给用户显示一个小通知
showToast('You have unsaved changes, discard and leave anyway?')
}
也可用 .then 的写法
// 正在尝试访问 admin 页面
router.push('/admin').then(failure => {
if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
failure.to.path // '/admin'
failure.from.path // '/'
}
})
failure 对象中的 to 和 from 都是规范化的路由地址。
全局路由故障检测
router.afterEach((to, from, failure) => {
if (failure) {
sendToAnalytics(to, from, failure)
}
})
检测重定向
通过读取路由地址中的 redirectedFrom 属性实现
await router.push('/my-profile')
if (router.currentRoute.value.redirectedFrom) {
// redirectedFrom 是解析出的路由地址,就像导航守卫中的 to和 from
}
标签:实用教程,Vue,const,component,vue,path,router,最新版,路由
From: https://blog.csdn.net/weixin_41192489/article/details/140610904