除了 props
和 $emit
之外,路由时也可以携带数据,即通过路由传参。
1. 动态路由的匹配
示例需求:有个商品详情的组件,点击任何商品都会渲染这个组件,点击商品时向详情组件传递商品的 id,详情组件在被渲染时接收这个 id,请求对应的数据,渲染在详情组件上。
可以在配置路由时直接为参数预留路径:
// router>index.js 的routes
routes: [
{
// 在路径中预留参数的位置
path: '/good-detail/:id',
component: Detail
}
]
调用:
<router-link to="/good-detail/1">商品1router-link>
<router-link to="/good-detail/2">商品2router-link>
<router-link to="/good-detail/3">商品3router-link>
<router-view />
接收参数:
<div class="detail">
{{ this.$route.params.id }}
div>
// 如果想要在组件刚开始加载时获取 id,并且根据 id 做一些数据操作,那就在对应的钩子函数中,获取路由参数
created() { // detail 组件生命周期的 created 钩子函数
const id = this.$route.params.id;
// code
}
用 created
和 mounted
函数,只能在第一个商品被渲染时响应,detail
组件在第一次被渲染之后,虽然路由变了,但是 created
和 mounted
不会重新执行。这种情况下,可以用 updated
来动态获取路由参数。也可以用 watch
来监听路由的变化。
watch: {
// to 和 from 分别表示路由切换前后的路由元信息
'$route'(to, from) {
// 对路由变化作出响应...
}
}
2. router-link 的 to属性传参
如果使用
标签的 to
属性动态传参,那么路由配置时不需要在 path
路径中预留参数的位置,直接传参并接收就可以了。
// router>index.js 的routes
routes: [
{
// 路径中不需要预留参数的位置
path: '/good-detail',
component: Detail
}
]
传参:
<router-link
:to="{
path: '/good-detail',
query: {
id: 1
}
}"
>商品3router-link>
接收参数:
// detail 组件的 created 钩子函数
created() {
const id = this.$route.query.id;
// code
}
这样传参,有个好处,就是有多个参数时,无论是配置还是传参,代码都非常简洁,如果有多个参数,直接列在 query
对象中就可以了。
但是也有个缺点,就是传递的参数会暴露在 url
中,如果参数中的信息是需要保护的信息,那么直接暴露就没有任何安全性了。
这就要用到 params
,params
和 query
的区别就是 params
中的参数不会暴露在 url
中,query
会。
但是 params
需要和路由的 name
结合使用。
// router>index.js 的routes
routes: [
{
path: '/good-detail',
name: 'good-detail',
component: Detail
}
]
<router-link
:to="{
name: 'good-detail',
params: {
id: 1
}
}"
>商品3router-link>
获取 params
中的参数:
// detail 组件的 created 钩子函数
created() {
const id = this.$route.params.id;
// code
}
注:
path
只能和 query
结合使用,name
可以和 query
或者 params
结合使用,但是 name
和 query
结合使用时,参数依旧会暴露。
3. push 时传参
// path 和 query 结合使用
this.$router.push({
path: '/good-detail',
query: {
id: 1
}
});
// name 和 query 结合使用
this.$router.push({
name: 'good-detail',
query: {
id: 1
}
});
// name 和 params 结合使用
this.$router.push({
path: '/good-detail',
params: {
id: 1
}
});
接受参数方式不变。
1. 导航守卫
通俗点说,导航守卫指在路由切换时,对组件中的数据或者用户信息做一些保护和判断。
比如用户从 A 组件切换到 B 组件,但是 B 组件中的内容是要根据用户的权限渲染的,不同权限的用户渲染不同的内容。就需要在切换之前拿到用户的权限信息,做判断之后再切换组件。
还有一种可能性,某个后台管理系统只能在用户登录之后才展示相对应的页面,但是用户可能已经知道了首页的路径,用户可能没经过登录直接在浏览器输入首页路径,要强行进入首页,那么在此之前,我们要做导航守卫。
其实我们可以把导航守卫理解成路由过程中的钩子函数。
全局守卫
// router>index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({
routes: [
// ...
]
});
// 全局前置守卫
router.beforeEach((to, from, next) => {
// code
});
// 全局后置钩子
router.afterEach((to, from) => {
// ...
});
export default router
全局的守卫会在每个路由发生变化时响应,但是有时候我们只需要在某个路由发生变化时做一些操作,并不需要所有路由全部响应切换。全局守卫的循环还是稍微有点耗性能。
某个路由自己的守卫
import Vue from 'vue'
import Router from 'vue-router'
import GoodDetail from '@/components/GoodDetail'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/good-detail',
component: GoodDetail,
beforeEnter: (to, from, next) => {
// 只有当前路由切换时要响应的代码
// 和其他路由没有任何关系
}
}
]
})
如果需要在某个组件中,捕获到路由变化之后或者之前,对数据做一些保留或清除动作。
比如从商品列表切换到商品详情,我们可以在详情组件路由切换成功的钩子函数中,获取路由参数,就可以使用组件中的路由守卫(当然也可以用组件生命周期钩子函数,也可以使用watch,这里组件中的路由守卫只是提供另一种可能性)。
组件内的守卫
// 某个组件的 export
export default {
name: 'Detail',
// vue-router 文档提供的示例
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
注:
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
完整的导航流程解析
官网给了导航切换的整个细致流程及每个阶段的钩子函数。
to, from, next
在上面的很多例子中,我们都能看到 钩子函数接收的参数,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() 注册过的回调。
2. 路由元信息
在配置路由时,路由对应的某些固定数据,我们可以直接绑定在路由配置对象的 meta
对象中,然后在需要时获取。
// router>index.js 的 routes
routes: [
{
path: '/good-detail',
component: GoodDetail,
// 定义路由的时候可以配置 meta 字段:
meta: {
title: '商品详情',
ifSee: true
}
}
]
获取路由元信息:
// Detail 组件的 created
created() {
console.log(this.$route.meta);
}
我们在运行项目时,能看到 url
中带有一个 #
,这个是 vue-router
默认的 hash 模式。因为正常情况下,切换浏览器的 url
浏览器中的内容是要刷新的,但是我们希望的是通过在 url
中切换路由来实现页面组件的切换,所以 vue-router
在 url
中添加了 #
号,这样在切换路由导致 url
改变时不会刷新页面。其实
标签最终渲染的真实 DOM
是 ,
to
属性是 标签的
href
,#
是锚点:
<a data-v-957c9522="" href="#/good-detail" class="router-link-exact-active router-link-active">详情a>
如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id。
只是设置成 history
之后,一些与后台的数据交互需要依赖后台配置。