Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
我们经常需要把某种模式匹配到的所有路由,全都映射到同一个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
const User = {
template: 'User'
}
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User
}
]
})
这样 /user/you
和 /user/me
都将映射到地址为 /user
的路由
像这种“路径参数”使用 :
标记。当匹配到一个路由时,参数值会被设置到 this.$route.params
,可以在每个组件内使用。于是,我们可以更新 User 的模板,输入当前用户的ID:
const User = {
template: 'User {{ $route.params.id }}'
}
可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params
中。例如:
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/Lee | { username: ‘Lee’ } |
/user/:username/age/:age_num | /user/Lee/age/18 | {username: ‘Lee’, age: 18} |
一些时候,我们可能有这样的需求,一个组件我们可以设置一个参数,但是这个参数它不是必须的:
const router = new VueRouter({
router: [
{
path: '/user/:id',
component: User
}
]
});
按照上面的方式定义 router 路径,如果我们需要跳转到 /user
路径的话,那么我们跳转时必须携带一个id
,就是说我们只能 /user/1
这样才能正确的匹配到 /user/:id
这个路由,那么怎样才能把 :id
变成一个可选参数呢? 很简单,只需要在 :id
后面添加一个 ?
const router = new VueRouter({
router: [
{
path: '/user/:id?',
component: User
}
]
});
这样,我们访问 /user
时就可以根据需求来决定是否需要添加这个id, /user、/user/1
我们该如何获取路由跳转时所携带的参数呢?
const router = new VueRouter({
router: [
{
path: '/user/:id?',
component: User
}
]
});
this.$router.push({
path: '/user',
query: {
id: 2
}
});
// 此时浏览器路径为:/user?id=2
console.log(this.$route.query) // {id:2}
this.$router.push({
name: 'user',
params: {
id: 2
}
});
// 此时浏览器路径为: /user/2
console.log(this.$route.params) // {id:2}
上述代码中,我们发现了一个问题。有的时候,我们要使用 $router.params
来获取参数,有的时候我们又要使用 $router.query
来获取参数,那么到底怎么区分呢?
实际上,上述代码已经展示了它们的异同: 当我们使用 path
进行路由跳转时,我们是通过 query
添加参数的,而使用 name
进行路由跳转时,我们是通过 params
进行添加参数的。
那么如果 path
通过 params
添加参数是否可行呢?答案是:NO、NO、NO。
但是,我们使用 name
进行路由跳转时,却可以使用 query
来添加参数:
const router = new VueRouter({
router: [
{
path: '/user/:id?',
component: User
}
]
});
this.$router.push({
path: '/user',
// 使用 path 时,会自动忽略 params 属性
params: {
id: 2
}
});
// 此时浏览器路径为:/user
console.log(this.$route.query) // {}
console.log(this.$route.params) // {id: undefined}
this.$router.push({
name: 'user',
// 使用 name 可以同时定义 query、params 两种传参方式
query: {
name: 'Lee'
},
params: {
id: 1
}
});
// 此时浏览器路径为: /user/1?name=Lee
console.log(this.$route.query) // {name: 'Lee'}
console.log(this.$route.params) // {id: 1}
另外,params 中的参数,当用户刷新当前页面时,就会丢失,而 query 则不会
params 传参,相当于http中的 post 请求
query 传参,相当于http中的 get 请求
不是说 params
相当于 post 吗?那么上述示例中的代码,为什么 params
中的 id
会在路径里面显示出来?那是因为在定义路由时,我们通过 /:id
把它具化了,如果把这个参数去掉,我们在浏览器地址栏中是不存在的,但是可以通过 $route.params
取到参数
const router = new VueRouter({
router: [
{
path: '/user/:id?',
component: User
}
]
});
this.$router.push({
name: 'user',
params: {
id: 2,
name: 'Lee'
}
});
// 此时浏览器路径为: /user/2
// 我们只能看到已经具化的id,但是看不到name
// 但是我们可以通过 this.$route.params 得到 name
console.log(this.$route.params) // {id: 2, name: 'Lee'}
当使用路由参数时,例如从 /user/foo
导航到 /user/bar
,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用组件时,想对路由参数的变化作出响应的话,可以使用 watch
来监听 $route
对象:
const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
}
}
或者使用 beforeRouteUpdate
导航守卫:
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
...
next() // 一定要记得执行next方法
}
}
定义路由的时候可以配置 meta
字段:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
meta: { requiresAuth: true }
}
]
}
]
})
那么如何访问这个 meta 字段呢?
首先,我们称呼 routes
配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录。
例如,根据上面的路由配置,/foo/bar
这个 URL 将会匹配父路由记录以及子路由记录。
一个路由匹配到的所有路由记录会暴露为 $route
对象 (还有在导航守卫中的路由对象) 的 $route.matched
数组。因此,我们需要遍历 $route.matched
来检查路由记录中的 meta 字段。
下面例子展示在全局导航守卫中检查元字段:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// 如果当前这个路由需要鉴权,则检查是否已登录,如没有登录,则跳转登录
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。