大前端百科全书vue专题之vue-router

435 阅读7分钟

大前端百科全书,前端界的百科全书,记录前端各相关领域知识点,方便后续查阅及面试准备

  • 说一下vue-router的原理是什么?
  • 介绍下 vue-router 中的导航钩子函数
  • 完整的路由导航解析流程(不包括其他生命周期)
  • 说一下路由钩子在 vue 生命周期的体现?
  • Vue 切换路由的时候,需要保存当时状态的功能,怎么实现呢?
  • Vue-router history 模式部署的时候要注意什么?nginx 和 node 时候分别怎么处理?

说一下vue-router的原理是什么?

实现原理:vue-router的原理就是更新视图而不重新请求页面

vue-router可以通过mode参数设置为三种模式:hash模式、history模式、abstract模式。

hash模式

默认是hash模式,基于浏览器history api,使用

window.addEventListener('hashchange', callback, false)

对浏览器地址进行监听

当调用push时,把新路由添加到浏览器访问历史的栈顶。

使用replace时,把浏览器访问历史的栈顶路由替换成新路由。

hash的值等于url中#及其以后的内容。

浏览器是根据hash值的变化,将页面加载到相应的DOM位置。

锚点变化只是浏览器的行为,每次锚点变化后依然会在浏览器中留下一条历史记录,可以通过浏览器的后退按钮回到上一个位置

history模式

基于浏览器history api ,使用window.onpopstate对浏览器地址进行监听。

对浏览器history api中的pushState()replaceState()进行封装,当方法调用,会对浏览器的历史栈进行修改。从而实现URL的跳转而无需加载页面。

但是他的问题在于当刷新页面的时候会走后端路由,所以需要服务端的辅助来兜底,避免URL无法匹配到资源时能返回页面

abstract

不涉及和浏览器地址的相关记录。流程跟hash模式一样,通过数组维护模拟浏览器的历史记录栈,服务端下使用。使用一个不依赖于浏览器的浏览器历史虚拟管理后台

总结 hash模式和history模式都是通过window.addEvevtListenter()方法监听 hashchange和popState进行相应路由的操作。可以通过back、foward、go等方法访问浏览器的历史记录栈,进行各种跳转。而abstract模式是自己维护一个模拟的浏览器历史记录栈的数组

介绍下 vue-router 中的导航钩子函数

全局的钩子函数 beforeEach 和 afterEach

beforeEach 有三个参数,to 代表要进入的路由对象,from 代表离开的路由对象。next 是一个必须要执行的函数,如果不传参数,那就执行下一个钩子函数,如果传入 false,则终止跳转,如果传入一个路径,则导航到对应的路由,如果传入 error ,则导航终止,error 传入错误的监听函数

单个路由独享的钩子函数 beforeEnter

它是在路由配置上直接进行定义的

组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。

它们是直接在路由组件内部直接进行定义的

完整的路由导航解析流程(不包括其他生命周期)

  • 触发进入其他路由。
  • 调用要离开路由的组件守卫 beforeRouteLeave
  • 调用全局前置守卫: beforeEach
  • 在重用的组件里调用 beforeRouteUpdate
  • 调用路由独享守卫 beforeEnter
  • 解析异步路由组件
  • 在将要进入的路由组件中调用 beforeRouteEnter
  • 调用全局解析守卫 beforeResolve
  • 导航被确认
  • 调用全局后置钩子的 afterEach 钩子
  • 触发 DOM 更新(mounted)
  • 执行 beforeRouteEnter 守卫中传给 next 的回调函数

触发钩子的完成顺序

路由导航、keep-alive、 组件生命周期钩子结合起来的,假设是从 a 组件离开,第一次进入 b 组件,触发顺序:

  • beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。
  • beforeEach:路由全局前置守卫,可用于登录验证、全局路由 Loading 等。
  • beforeEnter: 路由独享守卫
  • beforeRouteEnter: 路由组件的组件进入路由前钩子
  • beforeResolve:路由全局解析守卫
  • afterEach:路由全局后置钩子
  • beforeCreate 组件生命周期,不能访问 this
  • created:组件生命周期,可以访问 this, 不能访问dom
  • beforeMount:组件生命周期
  • deactivated: 离开缓存组件 a,或者触发 a 的beforeDestroy 和 destroyed 组件销毁钩子
  • mounted:访问或者操作 dom
  • activated:进入缓存组件,进入a的嵌套子组件(如果有的话)
  • 执行beforeRouteEnter回调函数next。

说一下路由钩子在 vue 生命周期的体现?

Vue-Router 导航守卫

有的时候,我们需要通过路由来进行一些操作,比如最常见的登录权限验证,当用户满足条件时,才让其进入导航,否则就取消跳转,并跳到登录页面让其登录。

为此我们有很多种方法可以植入路由的导航过程:全局的,单个路由独享的,组件级的

全局路由钩子

vue router 全局有三个路由钩子:

  • router.beforeEach 全局前置守卫 进入路由之前
  • router.beforeResolve 全局解析守卫(2.5.0+) 在 beforeRouteEnter 调用之后调用
  • router.afterEach 全局后置钩子 进入路由之后

具体使用:

  • beforeEach (判断是否登录, 没登录就跳转到登录页)
router.beforeEach((to, from, next) => {
  let ifInfo = Vue.prototype.$common.getSession("userData"); // 判断是否登录的存储信息
  if (!ifInfo) {
    // sessionStorage里没有储存user信息
    if (to.path == "/") {
      //如果是登录页面路径,就直接next()
      next();
    } else {
      //不然就跳转到登录
      Message.warning("请重新登录!");
      window.location.href = Vue.prototype.$loginUrl;
    }
  } else {
    return next();
  }
});
  • afterEach(跳转之后滚动条返回顶部)
router.afterEach((to, from) => {
  // 跳转之后滚动条回到顶部
  window.scrollTo(0, 0);
});

单个路由独享钩子

  • beforeEnter

如果不想全局配置守卫的话,可以为某些路由单独配置守卫

export default [
  {
    path: "/",
    name: "login",
    component: login,
    beforeEnter: (to, from, next) => {
      console.log("即将进入登录页面");
      next();
    },
  },
];

组件内的钩子

  • beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
  • beforeRouteEnter: 进入组件前触发
  • beforeRouteUpdate: 当前地址改变并且该组件被复用时触发,举例来说,带有动态参数的路径 list/:id,在/list/1 和/list/2 之间跳转的时候,由于会渲染同样的 list 组件,这个钩子在这种情况下就会被调用
  • beforeRouteLeave: 离开组件被调用

注意:beforeRouteEnter 组件内还访问不到 this,因为该守卫执行前组件实例还没有被创建,需要传一个回调给 next 来访问,比如:

beforeRouteEnter(to, from, next) {
    next(target => {
        if (from.path == '/classProcess') {
            target.isFromProcess = true
        }
    })
}

beforeRouteUpdate 和 beforeRouteLeave 可以访问组件实例 this

Vue 切换路由的时候,需要保存当时状态的功能,怎么实现呢?

beforeRouteLeave

beforeRouteLeave(to,from,next){
    if('用户已经输入了信息'){
        // 出现弹窗提醒保存草稿,或者自动后台为其保存
        // do something
    }else{
        next(true);// 用户离开
    }
}

这里可以把数据存储到vuex,缺点就是页面刷新数据会丢失

存储到localStorage并且和vuex关联

存储到数据库中

keep-alive

用keep-alive缓存路由

Vue-router history 模式部署的时候要注意什么?server 端用 nginx 和 node 时候分别怎么处理?

Vue-router 默认是 hash 模式,使用 url 的 hash 来模拟一个完整的 url,当 url 改变的时候,页面不会重新加载。比如:使用 hash 模式的时候,那么访问变成 http://localhost:8080/page/#/这样的访问。但是如果路由使用history的话,那么访问的路径变成如下http://localhost:8080/page/.

需要注意的问题

如果使用的是 history 这种模式,在非首页情况下刷新页面或直接访问的时候就会返回 404,导致页面丢失。

这是因为利用 H5 history API 来实现的。通过 history.pushState 方法实现 URL 的跳转而无需重新加载页面。但是它的问题在于当刷新页面的时候会走后端路由,相当于直接在浏览器里输入这个地址,要对服务器发起 http 请求,但是这个目标在服务器上又不存在这个路由,所以会返回 404

解决方式:需要服务端的辅助来兜底,避免 URL 无法匹配到资源时能返回页面

Nginx 配置

location / {
  try_files $uri $uri/ /index.html;
 }

node 配置

// 可以使用 connect-history-api-fallback 这个中间件
// npm install --save connect-history-api-fallback

// app.js
var history = require("connect-history-api-fallback");
var connect = require("connect");
var app = connect().use(history()).listen(3000);

// 或者使用 express
var express = require("express");
var app = express();
app.use(history());