1:导航钩子
vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:全局的, 单个路由独享的, 或者组件级的。
1:全局钩子
可以使用router.beforeEach注册一个全局的before钩子:
const router=new VueRouter({...})
router.beforeEach((to,form,next)=>{
defines.progressFlag=true;
NProgress.start();
next(); //必须写,否则不会出现任何东西
})
当一个导航触发时,全局的before钩子按照创建顺序调用。钩子是异步解析执行,此时导航在所有钩子resolve完成之前一直处于等待中;每个钩子方法接受3个参数:
(1)to:route :即将进入的目标路由对象
(2)form:route:当前导航正要离开的路由
(3)next:function:一定要调用该方法来resolve这个钩子,执行效果依赖next方法调用的参数;
A:next():进行管道中的下一个钩子,如果全部钩子执行完了,则导航的状态就是confirmed(确认的)
B:next(false):中断当前的导航,如果浏览器的URL改变了(可能是用户手动或者浏览器后退按钮),那么URL地址会重置到from路由对应的地址;
C:next(‘/’)或者next({path:’/’}):跳转到一个不同的地址,当前的导航被中断,然后进行一个新的导航;
确保调用next方法,否则钩子不会被resolved;
同样可以注册一个全局的after钩子,不过它不像before钩子那样,after钩子没有next方法,不能改变导航
router.afterEach(route=>{
//...
})
2:某个路由独享的钩子
可以在路由配置上直接定义beforeEnter钩子,这些钩子与全局before的方法参数是一样的;
const router=new VueRouter({
routes:[
path:'/foo',
component:FOO,
beforeEnter:(to,from,next)=>{
//...
}
]
})
3:组件内的钩子
路由组件内直接定义3个路由导航钩子
const Foo={
template:'...',
beforeRouteEnter(to,from,next){
//在渲染该组件的对应路由被confirm前调用;不能获取组件实例this,因为当钩子执行前,组件实例还没有被创建
},
beforeRouteUpdata(to,from,next){
//在当前路由改变,但是该组件被复用时调用;例如对于一个带有动态参数的路径 /foo/:id,在/foo/1跟foo/2之间跳转的时候,由于会渲染同样的foo组件,因此组件实例会被复用,二这个钩子就会在这个情况下被调用;可以访问组件实例this
},
beforeRouteLeave(to,from,next){
//导航离开该组件的对应路由时调用,可以访问组件实例this,这个leave钩子通常用来禁止用户在还未保存修改前突然离开。可以通过next(false)来取消导航;
}
}
对于beforeRouteEnter虽然不能使用this,但是可以通过传一个回调给next来访问组件实例;在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数;
beforeRouteEnter(to,from,next){
next(
vm=>{
//通过vm访问组件实例
}
)
}
2:路由元信息
定义路由的时候可以配置meta字段:
const router=new VueRouter({
routes:[
path:'/foo',
component:Foo,
children:[
path:'bar',
component:Bar,
meta:{ requiresAuth: true }
]
]
})
我们称呼routes配置汇总的每个路由对象为路由记录;路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可以匹配多个路由记录;一个路由匹配到所有路由记录会暴露为 route对象的 route.matched数组,因此,我们需要遍历$route.matched来检查路由记录中的meta字段;
3:过渡效果
<transition>
<router-view></router-view>
</transition>
单个路由的过渡
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
还可以基于当前路由跟目标路由的变化关系,动态设置过渡效果
<transition :name='transitionName'>
<router-view></router-view>
</transition>
//接着在父组件内,watch $route决定使用哪种过渡效果
watch:{
'$route'(to,from){
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
4:数据获取
有时候,在进入某个路由时,需要从服务器获取数据。例如:在渲染用户信息时,你需要从服务器获取用户数据,我们可以通过2种方式来实现:(技术角度讲,2种方式都不错,取决于你想要的用户体验是哪种)
1:导航完成之后获取
先完成导航,然后再接下来的组件生命周期钩子汇总获取数据;在数据获取期间显示loading
当使用这种方式时,我们会马上导航和渲染组件,然后再组件的created钩子中获取数据;
2:导航完成之前获取
导航完成前,在路由的enter钩子中获取数据,在数据获取成功后执行导航
5:滚动行为
使用前端路由,当切换到新路由时,想要页面滚动到顶部,或者是保持原先的滚动位置,vue-router能做到,而且做得更好;这个功能只在html5 history模式下可用
6:路由懒加载
当打包构建应用时,JS包会变得非常大,影响页面加载;我们,如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应的组件;这样就更加高效了;
结合VUE的异步组件跟webpack的code splitting feature,轻松实现路由组件的懒加载;我们要做的就是吧路由对应的组件定义成异步组件:
const Foo=resolve=>{
//require.ensure是wenpack的特殊语法,用来设置code-split point
require.ensure(['./Foo.vue'],()=>{
resolve(require('./Foo.vue'))
})
}
另外一种代码分块的语法,使用AMD风格的require,于是就更加简单了:
const Foo=resolve=>require(['./Foo.vue'],resolve)
不需要改变任何路由配置,跟之前一样使用Foo
const router=new VueRouter({
routes:[
{
path:'/foo',
component:Foo
}
]
})
有时候我们想把某个路由下的所有组件都打包在同个异步chunk中,只需要给chunk命名,提供require.ensure第三个参数作为chunk的名称:
const Foo = r => require.ensure([], () => r(require('./Foo.vue')), 'group-foo')
const Bar = r => require.ensure([], () => r(require('./Bar.vue')), 'group-foo')
const Baz = r => require.ensure([], () => r(require('./Baz.vue')), 'group-foo')
webpack将相同chunk下的所有异步模块打包到一个异步快里面–这也意味着我们无需明确指出require.ensure的依赖(传空数组就行)
普通方式
懒加载以后
3个路由减少了11KB左右