更多干货
router-link 与 router-view
router-link
<router-link :to="{ path: '/hello', component: HelloWorld }">hello</router-link>
<router-link :to="{ path: '/user/useradd' }">user</router-link>
以上是两种写法,根据参数还会有更多中写法。
经过测试:
-
有component参数时优先router-link中配置的component,没有时从js中配置取
-
path参数至关重要,灵活所在,/user/useradd 实际匹配了两个组件,分别是user和useradd
嵌套路由
{ path: '/user', component:user,
children:[
{path:'/user/useradd', component:useradd},
{path:'/user/userdelete', component:userdelete}
]
}
router-view
开发的时候有时候会遇到一种情况,比如 :点击这个链接跳转到其他组件的情况,通常会跳转到新的页面,蛋是,我们不想跳转到新页面,只在当前页面切换着显示,那么就要涉及到路由的嵌套了,也可以说是子路由的使用。
<router-view> 是用来渲染通过路由映射过来的组件,当路径更改时,<router-view> 中的内容也会发生更改
<router-link :to="{ path: '/hello', component: HelloWorld }">hello</router-link>
<router-link :to="{ path: '/user/useradd' }">user</router-link>
<router-view/>
当前看主要应用于单页面中,与router-link配合,渲染router-link 映射过来的组件
路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的url上使用,限制了其灵活性。
const User= {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
使用props将组件和路由解耦
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id, component: User, props: true }
// 对于包含命名视图的路由,你必须分别为每个命名视图添加porps选项:
{
path: '/user/:id',
component: {default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
这样便可以在任何地方使用该组件,使得该组件更易于重用和测试。
布尔模式
如果props被设置为true,route.params将会被设置为组件属性。
函数模式
const router = new VueRouter({
routes: [
{ path: '/search', component:SearchUser,props:(route)=>({ query:route.query.q }) }
]
})
Url: /search?q=vue会将 { query: “vue” }作为属性传递给SearchUser组件。
尽可能保持props函数为无状态的,因为它只会在路由发生变化时起作用。如果需要状态来定义props,请使用包装组件,这样vue才能对状态变化做出反应。
import Vue from 'vue'
import VueRouter from 'vue-router'
import Hello from './Hello.vue'
Vue.use(VueRouter)
function dynamicPropsFn (route) {
const now = new Date()
return {
name: (now.getFullYear() + parseInt(route.params.years)) + '!'
}
}
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/', component: Hello }, // No props, no nothing
{ path: '/hello/:name', component: Hello, props: true }, // Pass route.params to props
{ path: '/static', component: Hello, props: { name: 'world' }}, // static values
{ path: '/dynamic/:years', component: Hello, props: dynamicPropsFn }, // custom logic for mapping between route and props
{ path: '/attrs', component: Hello, props: { name: 'attrs' }}
]
})
new Vue({
router,
template: `
<div id="app">
<h1>Route props</h1>
<ul>
<li><router-link to="/">/</router-link></li>
<li><router-link to="/hello/you">/hello/you</router-link></li>
<li><router-link to="/static">/static</router-link></li>
<li><router-link to="/dynamic/1">/dynamic/1</router-link></li>
<li><router-link to="/attrs">/attrs</router-link></li>
</ul>
<router-view class="view" foo="123"></router-view>
</div>
`
}).$mount('#app')
HTML5 History模式
vue-router默认hash模式–使用URL的hash来模拟一个完整的URL,于是当URL改变时,页面不会重新加载。
如果不想要很丑的hash,我们可以用路由的history模式,这种模式充分利用history.pushState API来完成URL跳转而无需重新加载页面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
当使用history模式时,URL就像正常的url, 例如http:/yoursite.com/user/id.
不过这种模式需要后台配置支持,因为应用是个单页客户端应用,如果后台没有正确配置,当用户在浏览器直接访问http://oursite.com/user/id就会返回404
导航钩子
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:
-
全局的
-
单个路由独享的
-
或者组件级的
全局前置守卫: router.beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
全局后置钩子:router.afterEach
router.afterEach(route => {
// ...
})
路由独享的钩子
即单个路由独享的导航钩子,它是在路由配置上直接进行定义的
cont router = new VueRouter({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
组建内的导航钩子
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
const Foo = {
template: `...`,
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,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。
不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
完整的导航解析流程
-
导航被触发
-
在失活的组件里调用离开守卫
-
调用全局的 beforeEach 守卫
-
在重用的组件里调用 beforeRouteUpdate 守卫
-
在路由配置里调用 beforEnter
-
解析异步路由组件
-
在被激活的组件里调用 beforeRouteEnter
-
调用全局的 beforeResolve 守卫
-
导航被确认
-
调用全局的 afterEach 钩子
-
触发 DOM 更新
-
在创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
路由元信息
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航钩子中的 route 对象)的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})