Vue Router 是 Vue.js (opens new window) 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
使用
npm install vue-router
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能,然后在HTML文件内使用<router-link>来导航,其中to属性指向指定导航链接,<router-link>会被渲染为一个a标签。使用<router-view>来指明路由出口,即将路由匹配到的组件渲染到该位置。当<router-link>对应的路由匹配成功,将自动设置class属性.router-link-active
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
const app = new Vue({
router
}).$mount('#app')
我们可以在任何组件内通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由
动态路由
动态路由参数可以使用 : 标记,比如当我们需要编辑不用id的文章时可以
const User = {
template: '<div>edit</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/edit/:id', component: edit }
]
})
此时/edit/1和/edit/2都指向相同的路由,其中被 : 标记的路径参数可以被this.$route.params获取到
其中当路由参数改变时组件会被复用,并不是被销毁后再重新创建,也就是说组件的生命周期钩子不会再次被调用。
//通配符用来匹配任意路径,含有通配符的路由一般放在最后面
{
// 会匹配所有路径,通常用于客户端 404 错误
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}
嵌套路由时在被嵌套的组件内使用router-view来显示组件,路由配置如下:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
})
路由跳转(path和params同时使用params不生效)
router.push(location, onComplete?, onAbort?)
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
如果跳转到相同的动态路由地址则需要调用beforeRouteUpdate来响应这个变化,因为此时属于组件被复用,生命周期钩子不会被调用
router.replace(location, onComplete?, onAbort?)
不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。与<router-link :to="..." replace>
作用相同
router.go(n)
this.$router.go(-1) //返回上一级
这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。
一个路由需要使用多个组件时,需要使用命名router-view
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
路由重定向
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目标还可以是一个命名路由,也可以是一个返回路由的方法。
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// return 重定向的 字符串路径/路径对象
}}
]
})
路由组件传参
使用props将组件与路由解耦
const router = new VueRouter({
routes: [
{
path: '/search',
component: SearchUser,
props: route => ({ query: route.query.q })
}
]
})
此时会将query:"vue"作为属性传递给 SearchUser 组件,query会附加在域名后。
HTML5 History模式
vue-router默认使用hash模式,此外我们还可以使用history模式
const router = new VueRouter({
mode: 'history',
routes: [...]
})
导航守卫
vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
参数或查询的改变并不会触发进入/离开的导航守卫。
全局前置守卫
可以通过router.beforeEach来注册
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
当一个导航触发时,守卫是异步解析执行的,此时导航在所有守卫 resolve 完之前一直处于 等待中。
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
中断当前的导航:next(false)
跳转到一个不同的地址:next('/') 或者 next({ path: '/' })
确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错
全局解析守卫
通过router.beforeResolve注册,与router.beforeEach的区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫被调用。
全局后置钩子
通过router.afterEach注册,这些钩子不会接受 next 函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
路由独享守卫
在路由配置上直接定义,例如以下为beforeEnter守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内守卫
- beforeRouteEnter( 是支持给 next 传递回调的唯一守卫)
- beforeRouteUpdate
- beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`,但可以通过传一个回调给 next来访问组件实例。next((vm)=>{...})
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
定义路由的时候可以配置meta字段,可以通过遍历 $route.matched 来检查路由记录中的 meta 字段。
网友评论