安装下载

npm install vue-router@4

全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// --------------------  router.js ----------------
// 引入组件
const Home = () => import('./components/show.vue')

// 引入router
import { createRouter, createWebHistory } from "vue-router";
// 定义路由规则
const routes = [
{ path: '/', component: Home },
]

// 创建路由
const router = createRouter({
// 内部提供了 history 模式的实现。
history: createWebHistory(),
routes,
})
export {router}

// ------------------------- main.js ----------------
import { createApp } from "vue";
import App from "./App.vue";

import {router} from './router.js'


// 挂载路由
createApp(App).use(router).mount("#app");

自定义正则路由或 404 Not found 路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Home = () => import('./components/show.vue')

import { createRouter, createWebHistory } from "vue-router";
const routes = [
{ path: '/', component: Home },
]

export const routes = [
// 匹配 /o/3549
{ path: '/o/:orderId' },

// 匹配 /p/books
{ path: '/p/:productName' },

// /:orderId -> 仅匹配orderId为数字的情况
{ path: '/:orderId(\\d+)' },
]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Home = () => import('./components/show.vue')
const NotFound = () => import('./components/NotFound.vue')

import { createRouter, createWebHistory } from "vue-router";
const routes = [
{ path: '/', component: Home },
// 当所有路由都没匹配到时,进入此页面
{ path: '*', component: NotFound },
]

const router = createRouter({
history: createWebHistory(),
routes,
})eApp(App).use(router).mount("#app");

export {router}

导航守卫

全局前置守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Home = () => import('./components/show.vue')
const NotFound = () =>import('./components/NotFound.vue')

import { createRouter, createWebHistory } from "vue-router";
const routes = [
{ path: '/', component: Home },
{ path: '/:pathMatch(.*)*', component: NotFound },
]

const router = createRouter({
history: createWebHistory(),
routes,
})


router.beforeEach((to, from, next) =>{
// doSomething
// return true
// true为继续执行路由 false为中断此操作,里面也可以传入指定路由 也可以调用next来继续操作
return {
path: '/login',
}
})
export {router}

路由独享的守卫

你可以直接在路由配置上定义 beforeEnter守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const Home = () => import('./components/show.vue')
const NotFound = () => import('./components/NotFound.vue')

import { createRouter, createWebHistory } from "vue-router";
const routes = [
{
path: '/',
component: Home,
beforeEnter: (to, from) => {
// doSomething
return false
},
},
{ path: '/:pathMatch(.*)*', component: NotFound },
]

const router = createRouter({
history: createWebHistory(),
routes,
})

export {router}

在 setup 中访问路由

当前路由

因为我们在setup里面没有访问 this,所以我们不能再直接访问 this.$router 或 this.$route 。作为替代,我们使用 userouter 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useRouter, useRoute } from 'vue-router'

export default {
setup() {
const router = useRouter()
const route = useRoute()

function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query,
},
})
}
},
}

导航守卫

虽然你仍然可以通过 setup 函数来使用组件内的导航守卫,但 Vue Router 将更新和离开守卫作为 组合式 API 函数公开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

export default {
setup() {
// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
const answer = window.confirm(
'Do you really want to leave? you have unsaved changes!'
)
// 取消导航并停留在同一页面上
if (!answer) return false
})

const userData = ref()

// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteUpdate(async (to, from) => {
//仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
})
},
}

滚动行为

当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置时,可以使用VueRouter内置的scrollBehavior方法实现

1
2
3
4
5
6
7
8
9
10
11
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 始终在元素 #main 上方滚动 10px
return {
// 也可以这么写
// el: document.getElementById('main'),
el: '#main',
top: -10,
}
},
})

检测导航故障

在实际开发中 会有少数情况出现 路由跳转失败或者路由跳转到当前页面

比如

  • 用户当前尝试导航到当前
  • 一个导航守卫通过调用 return false 中断了这次导航
  • 一个导航守卫通过返回一个新的位置,重定向到其他地方 (例如,return ‘/login’)
  • 一个导航守卫抛出了error

此时我们需要判断路由是否导航到了预期的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//如果导航被阻止,导致用户停留在同一个页面上,由 router.push 返回的 Promise 的解析值将是 Navigation Failure。否则,它将是一个 falsy 值(通常是 undefined)。这样我们就可以区分我们导航是否离开了当前位置:
const navigationResult = await router.push('/home')

if (navigationResult) {
// 导航被阻止
} else {
// 导航成功 (包括重新导航的情况)
this.isMenuOpen = false
}

// Navigation Failure 是带有一些额外属性的 Error 实例,这些属性为我们提供了足够的信息,让我们知道哪些导航被阻止了以及为什么被阻止了。

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?')
}