在传统的MVC架构中,是由服务端对用户的请求进行拦截,并根据请求URL的不同来返回不同的页面,这种路由方式叫做服务端路由。与之相对的还有另外一种前端路由方式,即把这些路由的工作交给前端来实现,由前端来根据不同的请求返回不同的页面。在单页面应用中,由于大部分的页面结构相对固定,需要改变的内容只占其中很小一部分,所以更适合使用前端路由。
Vue Router 就属于前端路由,它是 Vue.js 官方的路由管理器,与 Vue.js 深度集成,让构建单页面应用变得易如反掌。
Vue Router 可以提供如下功能:
通过
npm install vue-router
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
Vue-Router用法示例
Vue-Router用法示例!
路由效果演示:
我们经常会碰到需要把匹配某种模式的多个路由指向同一个组件的情形,例如下面这样:需要根据传入的商品ID参数来跳转到对应商品的详情页面,此时商品详情组件的路由匹配模式就是 /goods/商品ID。
要实现这个效果,需要用到 vue-router 提供的“动态路径参数”(dynamic segment) ,一个“路径参数”使用冒号“:”标记,并会被设置到Vue 实例的 this.$route.params 属性中,例如:
模式 |
匹配路径 |
$route.params |
/user/:username |
/user/evan |
{ username: 'evan' } |
/user/:username/post/:post_id |
/user/evan/post/123 |
{ username: 'evan', post_id: '123' } |
路由匹配到的组件会被渲染到指定的 router-view 元素中,所以使用 vue-router 时需要提前定义好 router-view 元素。
例图中商品列表和商品详情的路由配置如下:
export default new Router({
mode: 'history', // hash or history
routes: [
{
path: '/',
name: 'GoodsList',
component: GoodList
},
{
path: '/goods/:goodsId',
name: 'GoodsDetail',
component: {
template: GoodsDetails
}
}
]
});
需要注意的是:当使用动态路由参数时,参数不同的同一类型的多个组件实例会复用同一个组件实例。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。如果想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
或者使用 2.2 版本中引入的 beforeRouteUpdate 监测路由参数的变化:
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
在 vue-router 还可以使用 “*” 星号通配符来匹配任意路径,例如:
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 的参数。它包含了 URL 通过通配符被匹配的部分:
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
嵌套路由指的是路由里面嵌套路由的情形,一个最常见的应用场景就是文件目录树:点击一级目录会弹出它下面的二级目录,点击二级目录会弹出它下面的三级目录。
在这里我们还是以商城为例,假设在商品详情页面中我们既可以查看商品的详情,也可以查看商品的评论,详情页面与评论页面之间通过一个面包屑按钮来切换。在这种情况下,我们需要在商品页中嵌套商品详情和商品评论两个子路由,并且从商品列表页跳转到商品页面的时候需要携带上商品ID参数。
export default new Router({
mode: 'history', // hash or history
routes: [
{
path: '/',
name: 'GoodsList',
component: GoodList
},
{
path: '/goods/:goodsId',
name: 'GoodsInfo',
component: GoodsInfo,
children: [
{
path: 'detail',
name: 'GoodsDetail',
component: {
template: detail
}
},
{
path: 'rates',
name: 'GoodsRates',
component: {
template: rates
}
}
]
}
]
});
此时,在GoodsInfo组件中也需要包含自己的
商品介绍
商品详情
商品评论
在 Vue 实例内部可以通过 $router 访问路由实例,因此我们还可以通过编写代码调用 router 实例的方法来实现路由。
下面列举了 router 中比较常用的实例方法:
// 字符串
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.push({ path: '/user', params: { userId }}) // -> /user
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
还是以上面例子中的 GoodsDetail 路由为例:
export default new Router({
mode: 'history', // hash or history
routes: [
{
path: '/goods/:goodsId',
name: 'GoodsDetail',
component: {
template: GoodsDetails
}
}
]
});
有时候,通过路由的 name 来标识一个路由会显得更方便一些,例如下面的代码:
点击查看商品
router.push({ name: 'GoodsDetail', params: { goodsId: 123 }})
对于一个组件或者页面包含有多个视图的情形,每一个视图都需要使用一个
在配置路由时,需要使用 components (注意这里有个 s)为每一个视图绑定一个组件:
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: GoodsDescription,
view2: GoodsBasic,
view3: GoodsImage
}
}
]
})
“重定向”的意思是,当用户访问 /a 时,URL将会被替换成 /b,然后匹配路由为 /b。重定向有以下三种设置方法:
// 方法一
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
// 方法二
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
// 方法三
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
“别名”的意思是:若 /a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
const User = {
template: 'User {
{ $route.params.id }}'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
推荐使用 props 将组件和路由解耦,这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。
const User = {
props: ['id'],
template: 'User {
{ id }}'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
如果 props 被设置为 true,route.params 将会被设置为组件属性。
如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。例如下面的例子,会将请求 URL /search?q=vue 中的 {query: 'vue'} 作为属性传递给 SearchUser 组件。
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
https://router.vuejs.org/zh/guide/
https://router.vuejs.org/zh/api/