vue-router及导航解析流程

77 阅读4分钟

vue-router 是 Vue.js 官方提供的路由管理器,它有两种路由模式:hash模式和history模式。

hash模式vue-router 默认的模式是hash模式,hash模式背后的原理是onhashchange事件。hash模式的特点在于hash出现在url中,但是不会被包括在HTTP请求中,对后端没有影响,不会重新加载页面。hash模式的url中会带有一个#符号,例如:http://www.abc.com/#/hellohash值为#/hellohash模式对于浏览器的兼容性比较好,也是SPA单页面应用的标配。

history模式history模式利用了HTML5 History Interface中新增的pushState()replaceState()方法(需要特定浏览器支持)。history模式的url相对于hash模式而言,更加美观。history模式使用浏览器的history API来管理路由状态,而不是依赖于URL中的哈希。在history模式下,URL看起来更加整洁,没有#符号。例如,http://example.com/user/profilehistory模式可以在不刷新整个页面的情况下,通过改变URL来切换页面,实现更加流畅的用户体验。但是history模式需要后台配置支持,如果后台没有正确配置,访问时会返回404。

vue-router 传参有两种方式:一种是路由参数,通过定义动态路由传递参数;另一种是通过query来传递参数。vue-router 中routerouter的区别在于:route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的namepathparamsquery等。routerVueRouter的一个对象,通过Vue.use(VueRouter)VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,包含了所有的路由包含了许多关键的对象和属性。例如history对象$router.push({path:’ /path’}),本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录。

导航解析流程:

  1. 导航被触发:用户点击链接、手动输入 URL 或通过代码进行路由跳转等操作,触发导航。

  2. 在失活的组件里调用离开守卫(beforeRouteLeave):如果当前有组件即将失活,会调用该组件内的beforeRouteLeave守卫。

  3. 调用全局的前置守卫(beforeEach):执行在路由实例对象中注册的全局前置守卫函数。

  4. 在重用的组件里调用beforeRouteUpdate守卫(2.2+):如果是在具有动态参数的路由中,且组件被复用时,会调用该守卫。

  5. 在路由配置里调用路由独享的守卫(beforeEnter):针对当前路由配置中定义的beforeEnter守卫进行调用。

  6. 解析异步路由组件:处理异步加载的路由组件。

  7. 在被激活的组件里调用beforeRouteEnter:执行激活组件中的beforeRouteEnter守卫。

  8. 调用全局的解析守卫(beforeResolve,2.5+):触发全局解析守卫函数。

  9. 导航被确认:完成所有守卫的检查和处理后,确认导航。

  10. 调用全局的后置钩子(afterEach):执行全局后置钩子函数,但该钩子不会接受next函数,也不会改变导航本身。

  11. 触发 DOM 更新:更新页面的 DOM 结构以显示目标组件。

  12. 用创建好的实例调用beforeRouteEnter守卫中传给next的回调函数:如果在beforeRouteEnter守卫中通过next传递了回调函数,此时会使用创建好的组件实例来调用该回调函数。

在这个流程中,导航守卫起到了关键的作用,它们可以用于权限控制、路由跳转前的准备工作、处理路由参数变化等。每个守卫函数都接收三个参数:to(即将要进入的目标路由对象)、from(当前导航正要离开的路由)、next(一个必须调用的函数,用于决定是否继续导航或进行其他操作)。

以下是一个示例,展示了如何设置导航守卫:

// 全局前置守卫(在路由的 index.js 页面设置)
router.beforeEach((to, from, next) => {
  // 进行一些全局的操作,如权限控制等
  if (满足某些条件) {
    next(); // 继续进行导航
  } else {
    // 中断导航或跳转到其他页面
    next('/login'); 
  }
});

// 路由独享的守卫(在路由配置项中定义)
{
  path: '/test',
  component: testComponent,
  beforeEnter: (to, from, next) => {
    // 进行路由独享的操作
    next(); 
  }
}

// 组件内的守卫(在组件中定义)
export default {
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    next(vm => { 
      // 通过 vm 访问组件实例
    });
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变但组件被复用时调用
    next(); 
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    const answer = window.confirm('是否确认离开当前页面');
    if (answer) {
      next(); 
    } else {
      next(false); 
    }
  }
};