创建一个路由实例的配置选项
使用 new Router 创建了一个新的路由实例,其配置项说明:
mode:路由模式,默认值 ‘hash’ 使用井号( # )作路由,值 ‘history’ 可利用 History API 来完成页面跳转且无须重新加载;
routes:具体的路由配置列表,用到的配置项说明:
path:路由的路径;
name:路由的名称;
component:对应的视图组件;
通过注入路由器,我们可以在任何组件内通过 this. r o u t e r 访 问 路 由 器 , 也 可 以 通 过 t h i s . router 访问路由器,也可以通过 this. router访问路由器,也可以通过this.route 访问当前路由
动态路由参数匹配
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: ‘evan’ } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: ‘evan’, post_id: ‘123’ } |
除了 r o u t e . p a r a m s 外 , route.params 外, route.params外,route 对象还提供了其它有用的信息,例如, r o u t e . q u e r y ( 如 果 U R L 中 有 查 询 参 数 ) 、 route.query (如果 URL 中有查询参数)、 route.query(如果URL中有查询参数)、route.hash 等等
相应路由参数的变化
当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
const User1 = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
//或者使用
const User2 = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
路由匹配优先级
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后
嵌套路由
const routes = [
{
path:'/user/:id',
component:()=>import('@/views/user/User'),
children:[
{
//默认子路由
// UserHome 会被渲染在 User 的 <router-view> 中
path:'',
name:'UserHome',
component:()=>import('@/views/user/UserHome'),
},
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path:'profile',
name:'UserProfile',
component:()=>import('@/views/user/UserProfile'),
}
],
}
];
编程式的导航
声明式 | 编程式 |
---|---|
<router-link :to="…"> | router.push(…) |
在 Vue 实例内部,你可以通过 r o u t e r 访 问 路 由 实 例 。 因 此 你 可 以 调 用 t h i s . router 访问路由实例。因此你可以调用 this. router访问路由实例。因此你可以调用this.router.push
router.push这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path,params 会被忽略,同样的规则也适用于 router-link 组件的 to 属性
注意: 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)
router.replace 跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录
声明式 | 编程式 |
---|---|
<router-link :to="…" replace> | router.replace(…) |
router.go(n)这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)
命名路由
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
router.push({ name: 'user', params: { userId: 123 }})
重定向和路由别名
const router = new VueRouter({
routes: [
{
path: '/a',
redirect: '/b'
}
]
})
const router2 = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
//别名的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构
路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性
通过 props 解耦,如果 props 被设置为 true,route.params 将会被设置为组件属性
导航守卫
记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
确保要调用 next 方法,否则钩子就不会被 resolved
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
路由独享守卫
在路由配置上直接定义 beforeEnter 守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内的守卫
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
路由元信息
路由过渡动效
数据获取
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。
滚动行为
// 指定滚动行为
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
// 有锚点时,滚动到锚点
return { selector: to.hash }
} else if (savedPosition) {
// 有保存位置时,滚动到保存位置
return savedPosition
} else {
// 默认滚动到页面顶部
return { x: 0, y: 0 }
}
}
路由懒加载
导航守卫
导航守卫分为全局导航守卫和路由导航守卫
router.beforeEach((to, from, next) => {
/* must call `next` */
})
router.beforeResolve((to, from, next) => {
/* must call `next` */
})
router.afterEach((to, from) => {})
//组件内的守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave