Vue Router 进阶笔记

Posted Liane

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue Router 进阶笔记相关的知识,希望对你有一定的参考价值。

一、导航守卫
完整的导航解析流程:

  1. 导航被触发
  2. 在失活的组件里调用beforeRouteLeave
  3. 调用全局的beforeEach
  4. 在重用的组件里调用beforeRouteUpdate
  5. 在路由配置里调用beforeEnter
  6. 解析异步路由组件
  7. 在被激活的组件里调用beforeRouteEnter
  8. 调用全局的beforeResolve
  9. 导航被确认
  10. 调用全局的afterEach
  11. 触发DOM更新
  12. 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。

参数或查询的改变并不会触发进入/离开的导航守卫。可以通过观察$route对象来应对这些变化,或使用beforeRouteUpdate的组件内守卫。
1、全局前置守卫
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:\'/\'})  /*跳转到一个不同的地址。*/
  next(error)  /*导航被终止,且错误会传递给router.onError()注册过的回调。*/
}

确保next函数在任何给定的导航守卫中都被严格调用一次。可出现多于一次,但只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。

/* BAD */
router.beforeEach((to, from, next) => {
  if (to.name !== \'Login\' && !isAuthenticated) next({ name: \'Login\' })
  /*如果用户未能验证身份,则 `next` 会被调用两次*/
  next()
})

/* GOOD */
router.beforeEach((to, from, next) => {
  if (to.name !== \'Login\' && !isAuthenticated) next({ name: \'Login\' })
  else next()
})

2、全局解析守卫
router.beforeResolve--与router.beforeEachj类似,区别为:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
3、全局后置钩子
router.afterEach()--无next(),也不会改变导航本身

router.afterEach(to,from)=>{
  //...
}

路由配置方式:
1、路由独享的守卫--直接在路由配置上定义beforeEnter守卫

const router = new VueRouter({
  routes: [
    {
      path: \'/user\',
      component: User,
      beforeEnter: (to,from,next)=>{}
    }
  ]
})

2、组件内的守卫
可以在路由组件内直接定义以下路由导航守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    /* 在渲染该组件的对应路由被 confirm 前调用*/
    /*不能!获取组件实例 `this`*/
    /*因为当守卫执行前,组件实例还没被创建,可以通过传一个回调给next来访问组件实例,仅该守卫支持此方法*/
    next(vm => {
      /*通过vm访问组件实例*/
    })  
  },
  beforeRouteUpdate(to, from, next) {
    /* 在当前路由改变,但是该组件被复用时调用*/
    /* 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候*/
    /* 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。*/
    /* 可以访问组件实例 `this`*/
    this.name = to.params.name
    next()
  },
  beforeRouteLeave(to, from, next) {
    /* 导航离开该组件的对应路由时调用*/
    /* 可以访问组件实例 `this`*/
    /*该守卫可以用来禁止用户在还未保存修改前突然离开,导航可以通过next(false)取消*/
    const answer = window.confirm(\'还没有保存,确定要离开吗\')
    if(answer){
      next()
    }else{
      next(false)
    }
  }
}

二、路由元信息-meta

路由登录拦截

/*1、在需要做登录验证的路由中设置meta*/
requireAuth值为true
const router = new VueRouter({
  routes = [
    {
      path: \'/detail\',
      name: \'detail\',
      component: Detail,
      meta: {
        requireAuth: true
      }
    },
    {
      path: \'/login\',
      name: \'login\',
      component: Login
    }
  ]
})

/*2、router.beforeEach()钩子函数,会在进入每个网页之前被调用,可在该钩子函数内做路由拦截*/
router.beforeEach((to,from,next) => {
  /*判断将进入的路由是否需要路由拦截*/
  if(to.matched.some(record => record.meta.requireAuth){
    /*vuex.state判断token是否存在*/
    if(store.state.token){
      /*已登录*/
      next()
    }else{
      next({
        path: \'/login\',
        query: {redirect: to.fullPath}
      })
    }
  } else {
    next()
  }
})

三、过渡动效
使用<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 -->
<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\'
  }
}

四、数据获取
在进入某个路由后,需要从服务器获取数据,有两种方式:
1、导航完成之后获取:
在路由组件的created钩子函数中调用获取数据的方法

export default{
  data (){
    return {
      loading: false
    }
  },
  created(){
    //组件创建完成后获取数据,此时data已经被observed了
    this.fetchData()
  },
  methods: {
    fetchData() {
      this.loading = true
      //发起数据请求,此时可以通过$route.params获取url上的数据
      getPost(this.$route.params.id,(res)=>{
        //...
      })
    }
  }
}

2、在导航前获取数据
在要跳转的路由组件中的beforeRouteEnter守卫中获取数据,当数据获取成功后只调用next()方法。

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方法。

const router = new VueRouter({
  routes: [...],
  scrollBehavior(to,from,savedPosition){
    //return 期望滚动到的位置
    //return {x: number, y: number}
    //滚动到顶部
    //return {x:0,y:0}
    
    //savedPosition在按下后退和前进按钮时可用
    if(savedPosition) {
      return savedPosition
    } else {
      return {x:0,y:0}
    }
  }
})

模拟滚动到锚点

scrollBehavior(to,from,savedPosition){
  if(to.hash) {
    return {
      selector: to.hash,
      //设置平滑滚动
      behavior: \'smooth\'
    }
  }
}

六、路由懒加载
为提高加载效率,使用懒加载,让路由被访问时才加载对应组件。需结合Vue异步组件和webpack

//1、将异步组件定义为返回一个promise的工厂函数。
const Foo = ()=>{
  Promise.resolve({
    //组件定义对象
  })
}
//2、使用import引入组件
import(\'./Foo.vue\')  //import返回一个promise

//可结合两者
const Foo = ()=> import(\'./Foo.vue\')
//路由在实例化时跟普通的一样

以上是关于Vue Router 进阶笔记的主要内容,如果未能解决你的问题,请参考以下文章

Vue-router 进阶

路由vue-router进阶

vue(20)路由使用小进阶

VSCode自定义代码片段11——vue路由的配置

VSCode自定义代码片段11——vue路由的配置

VSCode自定义代码片段11——vue路由的配置