Vue Router【实用教程】(2024最新版)vue3 路由管理

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-linkv-elsev-bind="$props"customv-slot="{ isActive, href, navigate }"><av-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 组件,其中的路径参数 12 为用户 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,},

组件,其中的路径参数 12 为用户 ID

路由重定向 redirect

指当用户访问 /home 时,URL 会被 / 替换,然后匹配成 /

// 重定向至 -- 目标路径
[{ path: '/home', redirect: '/' }]// 重定向至 -- 命名路由
[{ path: '/home', redirect: { name: 'homepage' } }]// 属性值可以是方法,以便获取路由参数
{// /search/screens -> /search?q=screenspath: '/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,// 用在链接里的 hrefhref,// 布尔类型的 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 storeconst userStore = useAuthStore()// ...
})

路由配置中定义路由独享守卫

  {path: '/users/:id',component: UserDetails,beforeEnter: (to, from) => {// reject the navigationreturn 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>

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 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('/').lengthconst fromDepth = from.path.split('/').lengthto.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 上方滚动 10pxreturn {// 也可以这么写// 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
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/49172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EasyMedia转码rtsp视频流flv格式,hls格式,H5页面播放flv流视频

EasyMedia转码rtsp视频流flv格式&#xff0c;hls格式 H5页面播放flv流视频 文章最后有源码地址 解决海康视频播放视频流&#xff0c;先转码后自定义页面播放flv视频流 先看效果&#xff0c;1&#xff0c;EasyMedia自带的页面&#xff0c;这个页面二次开发改动页面比较麻烦 …

牛客14666(优先屏障) + 牛客14847(Masha与老鼠)

文章目录 写在前面14666-优先屏障思路编程 14847-Masha与老鼠思路编程 写在前面 昨天刷的这两道题写了很久&#xff0c;特别是Masha与老鼠这道题&#xff0c;写了都快3个小时&#xff0c;主要还是理解代码逻辑有点难&#xff0c;不过写完之后感觉收获挺大的&#xff0c;给我以…

张高兴的 MicroPython 入门指南:(三)使用串口通信

目录 什么是串口使用方法使用板载串口相互通信 硬件需求电路代码使用板载的 USB 串口参考 什么是串口 串口是串行接口的简称&#xff0c;这是一个非常大的概念&#xff0c;在嵌入式中串口通常指 UART(Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器)。…

gitlab查询分支API显示不全,只有20个问题

背景 gitlab查询分支API需要查询所有分支&#xff0c;且分支数量大于20&#xff0c;但目前调用接口返回的branch最多就显示了20个 解决方案 根据GitLab的文档&#xff0c;查询分支API默认最多返回20个分支。如果要一次性显示80个分支&#xff0c;可以使用分页参数来获取所有…

Vue的单元测试和端到端测试:确保组件可靠性与应用完整性

引言 在软件开发过程中,测试是保证代码质量和应用稳定性的关键环节。Vue.js 作为流行的前端框架,提供了一套完善的测试工具和生态系统,支持开发者进行单元测试和端到端测试。本文将深入探讨如何为Vue组件编写单元测试,并讨论如何使用Cypress等工具进行端到端测试。 单元测…

node.js实现静态资源加载的方法——两种方法http及express

一.使用内置http实现静态资源加载 原生node要实现静态资源加载就需要借助其内置模块“fs”中的方法去读取静态资源&#xff0c;然后用response中的方法响应给用户 1.需要用到的模块 const http require(http); const fs require(fs); const path require(path);2.加载静态…

初识c++(string和模拟实现string)

一、标准库中的string类 string类的文档介绍&#xff1a;cplusplus.com/reference/string/string/?kwstring 1、auto和范围for auto&#xff1a; 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;后来这个 不重…

Cannot perform upm operation: connect ETIMEDOUT 34.36.199.114:443 [NotFound]

版本&#xff1a;Unity 2018 Windows 问题&#xff1a;打开 Package Manager&#xff0c;加载报错 尝试解决&#xff1a; 删除项目文件里的Packages下的mainfest.json文件&#xff0c;然后重新打开项目&#xff08;X&#xff09;重新登录 Unity 账号&#xff08;X&#xff09…

Kotlin 协程 — 基础

Kotlin 协程 — 基础 协程已经存在一段时间了&#xff0c;关于它的各种文章也很多。但我发现想要了解它还比较费时&#xff0c;所以我花了一段时间才真正理解了协程的基础知识以及它的工作原理。因此&#xff0c;我想分享一些我理解到的内容。 什么是协程&#xff1f; 协程代表…

vue项目实战速查记录

1.图片下载到本地 2.本地静态文件访问 3.元素大小相同,相互覆盖 1.图片下载到本地 实现原理:创建a标签,利用a标签下载属性. download(){const link document.createElement(a);link.href "图片地址";link.setAttribute(download, name);document.body.ap…

读论文《Hi-Net: Hybrid-fusion Network for Multi-modalMR Image Synthesis》

论文题目&#xff1a;Hi-Net:用于多模态磁共振图像合成的混合融合网络 论文地址&#xff1a;arxiv 项目地址&#xff1a;github 原项目可能在训练的时候汇报version的错&#xff0c;这是因为生成器和辨别器的优化有些逻辑错误&#xff0c;会改的话多加一个生成操作可以解决&…

React 学习——条件渲染、遍历循环、事件绑定

React特点&#xff1a; 声明式的设计高效&#xff0c;采用虚拟DOM来实现DOM的渲染&#xff0c;最大限度减少DOM的操作灵活&#xff0c;跟其他库灵活搭配使用JSX&#xff0c;俗称JS里面写HTML&#xff0c;JavaScript语法的扩展组件化&#xff0c;模块化&#xff0c;代码容易复用…

git 过滤LFS文件下载

git config --global filter.lfs.smudge "git-lfs smudge --skip -- %f" git config --global filter.lfs.process "git-lfs filter-process --skip" 恢复下载 git config --global filter.lfs.smudge "git-lfs smudge -- %f" git config --g…

pdf的下载,后端返回工作流,前端进行转换

前端将后端返回的工作流进行转换 项目中接触到了pdf的下载和预览的功能&#xff0c;记录一下~ 这里pdf的下载和预览的接口&#xff0c;后端返回的数据结构与其他的接口返回的数据结构有点不同&#xff0c;是直接返回的工作流&#xff0c;在控制台接口的响应预览内容大致是这样…

南京移动老年手机课堂助力社区老人智享生活

为帮助社区老年群体解决使用智能手机过程中遇到的问题&#xff0c;让老年人充分享受数字化、智能化带来的便利&#xff0c;同时积极营造敬老、爱老、助老的社会氛围&#xff0c;江苏移动南京分公司在鼓楼区龙蟠里社区新时代文明实践站开展了“学以‘智’用&#xff0c;‘智’享…

初学MySQl简单sql语句(1)

目录 SQL语句介绍&#xff1a; DDL创建数据库&#xff1a; char和varchar比较 数值类型 数据库存储引擎 数据库存储引擎——InnoDB 数据库存储引擎——MyISAM 数据库存储引擎-MyISAM 和InnoDB区别 修改和删除数据库表 数据库设计三大范式 一、什么是范式 二、约束作…

css实战案例1:顶部搜索

代码样式&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><div class"search_box"><!-- 搜索框--><div class"search">搜索…

【Linux】网络基础_2

文章目录 十、网络基础2. IP地址和MAC地址3. 端口号端口号和进程ID 4. 网络字节序 未完待续 十、网络基础 2. IP地址和MAC地址 IP协议有两个版本&#xff0c;IPv4和IPv6&#xff0c; 用的比较多的都是IPv4。IP地址是在IP协议中&#xff0c;用来标识网络中不同主机的地址&…

Android SurfaceFlinger——关联EGL三要素(二十七)

通过前面的文章我们得到了 EGL 的三要素——Display、Surface 和 Context。其中,Display 是一个图形显示系统或者硬件屏幕,Surface 代表一个可以被渲染的图像缓冲区,Context 包含了 OpenGL ES 的状态信息和资源,它是执行 OpenGL 命令的环境。下一步就是调用 eglMakeCurrent…

如何发现快速发现分析生产问题SQL

Performance Schema介绍 Performance Schema提供了有关MySQL服务器内部运行的操作上的底层指标。为了解释清楚Performance Schema的工作机制&#xff0c;先介绍两个概念。 第一个概念是程序插桩&#xff08;instrument&#xff09;。程序插桩在MySQL代码中插入探测代码&#xf…