Vue.js 是一个构建数据驱动的 Web 界面的渐进式框架,核心库叫做「Vue」,这个库只关注视图层,采用自底向上增量开发的设计。
Vue.js 官方提供了一个扩展插件 Vue Router,它允许构建 SPA(Single Page Application)单页面应用程序。Vue Router 的作用是将每个路由映射到一个组件,然后将组件渲染到 DOM 中。
Vue Router 允许开发人员在路由改变时添加「钩子函数」,并且支持许多不同类型的钩子。本篇文章将深入解析 Vue 路由的各种钩子函数及其作用。
一、全局前置守卫:beforeEach
使用 Vue Router 时,它提供了多个路由导航钩子,其作用类似于其他框架中路由的生命周期函数。其中,全局前置守卫 `beforeEach` 是最常用的一个。这个钩子函数会在路由即将改变前被调用。我们可以利用这个钩子函数,实现登录、权限验证等场景。
beforeEach 函数接收三个参数:
- to: 即将要进入的目标路由对象
- from: 当前导航正要离开的路由对象
- next: 调用该方法后,才能进入下一个钩子函数,否则路由将会被中断
const router = new Router({
routes: [
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true }
}
]
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!userLoggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
})
上面的代码实现了一个简单的登录权限校验,如果用户未登录,路由将被重定向到登录页;否则,用户将被跳转到下一步操作的路由。
二、全局后置钩子:afterEach
Vue Router 还提供了另外一个路由导航钩子函数 afterEach,这个函数会在路由改变之后被调用。它没有 next 参数,也就不能中断路由,但我们常常可以利用这个钩子函数,实现页面的统计、日志打印等操作。
router.afterEach((to, from) => {
logPageView(to.path)
})
上面的代码实现了一个简单的页面访问日志打印,每当路由改变后,将会记录下页面的相关信息。
三、组件内守卫
在 Vue Router 中,我们还可以使用组件内路由钩子,这些钩子函数作用于组件级别。常用的组件内路由钩子有这几个:
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
这些钩子每次都会在相应的路由导航期间触发。
3.1 beforeRouteEnter
beforeRouteEnter 钩子可以访问组件实例 `this`,但是此时组件实例尚未创建。beforeRouteEnter 钩子中,不能使用 `this.props` 访问组件 props。
如果需要访问组件 props,我们可以通过回调函数,将 props 作为参数来传递。
const Post = {
props: ['id', 'title'],
beforeRouteEnter (to, from, next) {
axios.get(`/api/post/${to.params.id}`)
.then(response => {
next(vm => {
vm.post = response.data
})
})
},
data () {
return {
post: null
}
},
template: `
{{ post.title }}
`
}
上面的代码实现了在路由载入 Post 组件时,请求相应的文章详情。通过 next 回调函数,我们可以将响应结果传递给组件实例的 data 属性。
3.2 beforeRouteUpdate
beforeRouteUpdate 钩子函数,能够监听路由变化,做出相应的更新操作。它也能够访问到组件实例 `this`。
const Post = {
beforeRouteUpdate (to, from, next) {
this.loadPost(to.params.id)
next()
},
methods: {
loadPost (id) {
axios.get(`/api/post/${id}`).then(response => {
this.post = response.data
})
}
},
data () {
return {
post: null
}
},
template: `
{{ post.title }}
`
}
上面的代码实现在路由改变时,通过 loadPost 方法更新组件实例的 data 属性,从而使得组件重新渲染。
3.3 beforeRouteLeave
beforeRouteLeave 钩子函数,在离开当前路由时,做出相应的操作。这个钩子函数不能访问组件实例 `this`,但是我们可以通过 `vm` 参数来访问组件实例。
const PostEditor = {
beforeRouteLeave (to, from, next) {
if (this.content.length > 0) {
const doLeave = window.confirm('你还有未提交的内容,确认离开?')
if (doLeave) {
next()
} else {
next(false)
}
} else {
next()
}
},
data () {
return {
content: ''
}
},
methods: {
save () {
axios.post('/api/post', { content: this.content })
}
},
template: `
`
}
上面的代码实现了在离开 PostEditor 组件时,检测是否有未提交的内容。如果有,将提示用户确认是否离开,如果用户确认离开,这个钩子函数返回 next();如果用户取消离开,这个钩子函数返回 next(false),路由中断不会离开当前路由。
四、路由独享守卫
路由独享守卫,也就是仅对特定路由进行守卫。我们可以在路由配置对象上,添加 `beforeEnter` 来实现这个特定守卫。
const router = new VueRouter({
routes: [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (!userLoggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
}
}
]
})
上面的代码实现了在用户进入 /admin 这个路由时,会触发独享守卫,检查用户是否已经登录。如果未登录,则会重定向到登录页面;否则,进入目标路由。