Vue router 提高技巧
路由元信息
routes
中配置的每个路由对象是一条路由记录,路由记录中有一个meta
字段,可以向这个字段中添加一些自定义的属性,可以在定义路由的时候配置meat
字段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, meta: { requiresAuth: true } } ] } ] })
|
访问这个字段可以在各种守卫中通过to
(或者组件中的$route
对象)中获取到这个字段$route.meat
有时路由匹配会匹配父级路由及子路有,一个路由匹配到的所有路由记录都会暴露为路由对象的matched
数组,数组的成员时匹配到的所有父级、子级路由的全部路由对象
下面这个例子,就是通过to.matched
访问所有记录,检查meat
中设定字段是否符合要求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { if (!auth.loggedIn()) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } } else { next() } })
|
过渡特效
可以用<transition>
包围<router-view>
,为组件添加过渡特效,这样会给所有路由设置一样的过渡效果
也可以基于当前路由与目标路由的变化关系,动态设置过渡特效:
1 2 3 4
| <transition :name="transitionName"> <router-view></router-view> </transition>
|
在父组件内,watch
组件的$route
路由对象,动态改变transitionName
1 2 3 4 5 6 7 8 9
|
watch: { '$route' (to, from) { const toDepth = to.path.split('/').length const fromDepth = from.path.split('/').length this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left' } }
|
数据获取
有时候进入某个路由后,需要从服务器获取数据,有两种方式:
(1)导航完成后获取
先完成导航,渲染组件,在接下来的组件的生命周期钩子(比如created
)中获取数据,这也是比较常用的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| export default { data () { return { data: null } }, created () { this.fetchData() }, watch: { '$route': 'fetchData' }, methods: { fetchData () { } } }
|
在组件中,通过$route.params
获取网络请求需要的参数,再watch
路由对象$route
(或者在beforeRouteUpdate
中)重新获取数据即可
(2)导航完成前获取
在导航转入新的路由之前获取数据,可以在目标组件的beforeRouterEnter
守卫中获取数据,数据获取成功后调用next
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| export default { data () { return { post: null, error: null } }, beforeRouteEnter (to, from, next) { getPost(to.params.id, (err, post) => { next(vm => vm.setData(err, post)) }) }, beforeRouteUpdate (to, from, next) { this.post = null getPost(to.params.id, (err, post) => { this.setData(err, post) next() }) }, methods: { setData (err, post) { if (err) { this.error = err.toString() } else { this.post = post } } } }
|
这种方式,相当于在前一个路由组件中获取下一个路由组件的数据,用户会停留在当前界面。应该在数据获取期间显示进度条用来提示用户,如果数据获取失败,也应该展示全局的错误提醒。
页面滚动
创建Router实例时,提供scrollBehavior
方法,来定义页面的滚动行为:
1 2 3 4 5 6
| const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { } })
|
scrollBehavior
方法需要返回滚动位置的对象信息:
1 2
| { x: number, y: number } { selector: string, offset? : { x: number, y: number }} // offset 只在 2.6.0+ 支持
|
scrollBehavior
方法的第三个参数savedPosition
仅当通过浏览器的『前进』『后退』按钮触发才可用,这时候返回savedPosition
,就会像浏览器原生表现的一样:
1 2 3 4 5 6 7
| scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
|
也可以模拟滚动到锚点的行为:
1 2 3 4 5 6 7
| scrollBehavior (to, from, savedPosition) { if (to.hash) { return { selector: to.hash } } }
|
也可以返回一个Promise来进行异步的滚动:
1 2 3 4 5 6 7
| scrollBehavior (to, from, savedPosition) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ x: 0, y: 0 }) }, 500) }) }
|