vue-router浅析

vue-router

动态路由匹配(Dynamic Route Matching)

路径参数

一个“路径参数”使用冒号 : 标记。

模式 匹配路径 $route.params
/user/:username /user/evan { username: ‘evan’ }
/user/:username/post/:post_id /user/evan/post/123 { username: ‘evan’, post_id: 123 }

匹配优先级

有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。

1
2
3
4
const routes = [
{ path: '/foo/bar', component: Foo },
{ path: '/foo/:id', component: Bar }
]

当路由参数为bar时,/foo/bar 匹配了两个路由,这时就就会遵循路由匹配优先级规则。

嵌套路由

一个被渲染组件同样可以包含自己的嵌套 <router-link :to=”…”> 。例如,在 Deatil 组件的模板添加一个 <router-link :to=”…”>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<router-link to="/">Home</router-link>
<h3>{{ msg }}</h3>
<div>{{ detailId }}</div>
<router-link :to="'/detail/'+detailId+'/performance'">业绩表现</router-link>
<router-link :to="'/detail/'+detailId+'/report'">咨询研报</router-link>
<router-view></router-view>
</div>
</template>

<script>
export default {
name: "detail",
computed: {
detailId() {
return this.$route.params.id;
},
msg() {
return `Welcome to ${ this.detailId } Detail`
}
}
};
</script>

定义嵌套路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
...
{
path: "/detail/:id",
name: "detail",
component: Detail,
children: [
{
// 当 /detail/:id/performance 匹配成功,
// Performance 会被渲染在 Detail 的 <router-view> 中
path: 'performance',
component: Performance
},
{
path: 'report',
component: InformationReport
},
]
}
...
}

编程式的导航(Programmatic Navigation)

router.push(location, onComplete?, onAbort?)

声明式 编程式
<router-link :to=”…”> router.push(…)
1
2
3
4
5
6
7
8
9
10
11
// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

router.replace(location, onComplete?, onAbort?)

跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式 编程式
<router-link :to=”…” replace> router.replace(…)

router.go(n)

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。

1
2
3
4
5
6
7
8
9
10
11
12
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

重定向和别名

重定向(redirect)

1
2
3
4
5
6
7
8
9
10
11
12
{
...
// 字符串路径
{ path: '/a', redirect: '/b' },
// 命名路由对象
{ path: '/a', redirect: { name: 'foo' }},
// 方法接收 目标路由 作为参数
{ path: '/a', redirect: to => {
// return 重定向的 字符串路径/路径对象
}}
...
}

别名(alias)

/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

1
2
3
4
5
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})

router.mode

类型: string
默认值: “hash” (浏览器环境) | “abstract” (Node.js 环境)
可选值: “hash” | “history” | “abstract”
history: 依赖 HTML5 History API 和服务器配置。

导航守卫(Navigation Guards)

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

全局守卫

使用 router.beforeEach 注册一个全局前置守卫:

1
2
3
4
5
6
7
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
// ...
})

export default router

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    例子,在每次路由跳转时判断用户的登录状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// router.js
...
Vue.use(VueRouter);
const router = new VueRouter(appRouter);
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// 判断是否登录,已登录next,否则跳转至 login
if (Cookie.get("isLogin") === "1") {
next();
} else {
next("/login");
}
} else {
next();
}
});
export default router;

确保要调用 next 方法,否则钩子就不会被 resolved。

全局后置钩子

全局后置钩子与守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

1
2
3
router.afterEach((to, from) => {
// ...
})

过渡动效

单个路由过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}

const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}

基于路由的动态过渡

1
2
3
4
<!-- 使用动态的 transition name -->
<transition :name="transitionName">
<router-view></router-view>
</transition>
1
2
3
4
5
6
7
8
// 接着在父组件内
watch: {
'$route' (to, from) {
// 根据 to和 from 决定使用哪种过渡
...
this.transitionName = 'fade'
}
}

路由懒加载

有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。

1
2
3
4
5
const Login = () => import(/* webpackChunkName: "group-login" */ './Login.vue')
const User = () => import(/* webpackChunkName: "group-user" */ './User.vue')
const Fund = () => import(/* webpackChunkName: "group-detail" */ './Fund.vue')
const Company = () => import(/* webpackChunkName: "group-detail" */ './Company.vue')
const Manager = () => import(/* webpackChunkName: "group-detail" */ './FunManager.vue')

Vue Transition 的所有功能在这里同样适用

vue-router demo

vue-router demo

源码解读

参考网址: Vue Router

人的一切痛苦,本质上都是对自己的无能的愤怒。
——王小波