【Vue深入】之路由router

432 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

vue深入系列包括以下内容,有兴趣的读者可以选择阅读:

【Vue深入】之虚拟DOM - 掘金 (juejin.cn)

【Vue深入】之DIFF算法 - 掘金 (juejin.cn)

【Vue深入】之生命周期 - 掘金 (juejin.cn)

【Vue深入】之响应式原理 - 掘金 (juejin.cn)

【Vue深入】之Vuex状态管理 - 掘金 (juejin.cn)

引言

现如今Vue已成为当下主流框架,其中一些核心概念更是面试须知,本文主要进行对路由router进行介绍,希望能够对大家有所帮助。

路由模式

Hash模式

vue-router默认使用是便是Hash模式,Hash模式主要是通过url中的hash值来变化的。Hash(即#)是url的一个锚点,代表的是网页中的一个位置,当hash值变化时,浏览器就滚动到相应的位置,所以不会重新加载页面。在hash值变化的同时url会被浏览器记录下来,这样既可以使用浏览器的后退了。

History模式

自HTML5新标准出台,pushState和replaceState是HTML5的新接口,通过这两个 API 可以改变 url 地址且不会发送请求,前端路由从此了多了另外一种模式History,而且通过这种模式不再需要在URL添加#符号,也能让URL显得更加优美

两种模式的区别:

hash 模式:

即地址栏URL中的#符号。例如:www.abc.com/#/hello

访问一个不存在的路径不会发送请求

history 模式:

即地址栏URL中没有#符号。例如:www.abc.com/hello

访问一个不存在的路径会发送请求

注意: 打包后前端自测需要采用hash模式,如果采用history模式会出现空白页

路由跳转方式

router-link

1. 不带参数
<router-link :to="{name:'home'}">
    <router-link :to="{path:'/home'}"> //name,path都行, 建议用name 
    // 注意:router-link中链接如果是'/'开始就是从根路由开始,如果开始不带'/',则从当前路由开始。
    2.带参数
<router-link :to="{name:'home', params: {id:1}}">
    // params传参数 (类似post)
    // 路由配置 path: "/home/:id" 或者 path: "/home:id" 
    // 不配置path ,第一次可请求,刷新页面id会消失
    // 配置path,刷新页面id会保留
    // html 取参 $route.params.id
    // script 取参 this.$route.params.id
    <router-link :to="{name:'home', query: {id:1}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id
// script 取参 this.$route.query.id

this.$router.push()

1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'1'}}) // 只能用 name

// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
4. query和params区别
query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失

this.$router.replace() (用法同上,push)

this.$router.go(n) ()

向前或者向后跳转n个页面,n可为正整数或负整数

区别

  • this.$router.push:跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
  • this.$router.replace:跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
  • this.$router.go(n):向前或者向后跳转n个页面,n可为正整数或负整数
  • this.$route.path:获取当前路由地址

导航守卫

全局前置守卫

你可以使用router.beforeEach注册一个全局前置守卫:

从名字全局前置守卫不难理解,它是全局的,即对 整个单页应用(SPA) 中的所有路由都生效,所以当定义了全局的前置守卫,在进入每一个路由之前都会调用这个回调。

应用场景

在View Admin中全局前置守卫的应用场景就是对用于访问网页的情况进行相应的跳转,例如初次登入,不能访问除登录页面外的页面,已登录会自动跳转自首页等等。

router.beforeEach((to, from, next) => {
  //开启懒加载
  iView.LoadingBar.start()
  //获取token
  const token = getToken()
  if (!token && to.name !== LOGIN_PAGE_NAME) {
    // 未登录且要跳转的页面不是登录页
    next({
      name: LOGIN_PAGE_NAME // 跳转到登录页
    })
  } else if (!token && to.name === LOGIN_PAGE_NAME) {
    // 未登陆且要跳转的页面是登录页
    next() // 跳转
  } else if (token && to.name === LOGIN_PAGE_NAME) {
    // 已登录且要跳转的页面是登录页
    next({
      name: 'home' // 跳转到homeName页
    })
  }
})
  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由对象

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    • next() : 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false) : 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next('/') 或者 next({ path: '/' }) : 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    • next(error) : (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

注意:next 方法必须要调用,否则钩子函数无法 resolved

全局后置守卫

你可以使用router.afterEach注册一个全局后置守卫:

全局后置守卫是整个单页应用中每一次路由跳转后都会执行其中的回调。所以多用于路由跳转后的相应页面操作,并不像全局前置守卫那样会在回调中进行页面的重定向或跳转。

应用场景

应用场景前面以及提了就是对跳转后的页面进行例如滚动条回调0 0 位置、更新页面title、懒加载结束等等

router.afterEach((to, from) => {
  setTitle(to, router.app)
  iView.LoadingBar.finish()
  window.scrollTo(0, 0)
})

路由独享的守卫

你可以使用beforeEnter注册一个路由独享的守卫: 写在路由配置中,只有访问到这个路径,才能触发钩子函数

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {

      }
    }
  ]
})

组件内的守卫

beforeRouteEnter 进入组件之前

组件内的前置守卫beforeRouteEnter((to,from,next)=>{}) 导航进入组件时,调用因为组件此时没有创建,所以没有thisthis是访问不到的,如果非要访问this ,必须通过next(vm=>{}) 访问(因此next(vm=>{}) 时在mounted执行之后执行) 不过,你可以通过传一个回调给 next来访问组件实例,也就是说可以通过 next 来回调实例化后的组件,用next函数的 vm 参数充当 this

export default {
  name: "Admin",
  data(){
    return{
      infor:'hw'
    }
  },
  beforeRouteEnter:(to,from,next)=>{
    //此时该组件还没被实例化
    alert(this.infor);       //弹出消息框信息为 undefined
    next(vm =>{
      //此时该组件被实例化了
      alert(vm.infor);         //弹出消息框信息为 hw
    })
  }
}

应用场景:页面数据需要缓存的情况下可以使用beforeRouteEnter与activated结合使用

举例: keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

钩子函数的执行顺序

  1. 不使用keep-alive:beforeRouteEnter --> created --> mounted --> destroyed
  2. 使用keep-alive:beforeRouteEnter --> created --> mounted --> activated --> deactivated 再次进入缓存的页面,只会触发beforeRouteEnter -->activated --> deactivated 。created和mounted不会再执行。我们可以利用不同的钩子函数,做不同的事。
beforeRouteEnter(to, from, next) {
  //需要缓存的页面
  if (from.name === 'PFANS1024FormView') {
    to.meta.isBack = true;
  } else {
    to.meta.isBack = false;
  }
  next();
},
activated() {
  if (!this.$route.meta.isBack) {
    //  如果isBack是false,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
    this.init();
  }
  // 恢复成默认的false,避免isBack一直是true,导致下次无法获取数据
  this.$route.meta.isBack = false;

},

在需要缓存的页面的路由中设置meta.keepAlivetrue,isBack : false
{
  path: '/PFANS1024View',
      name: 'PFANS1024View',
    component: PFANS1024View,
    meta: {
  keepAlive: true  //需要被缓存
  isBack : false   //是否需要重新加载数据
}
},
在全局界面中设置<keep-alive> 包裹需要缓存的router

<keep-alive v-if="isRouterAlive">
    <router-view v-if="$route.meta.keepAlive">
    <!-- 这里是会被缓存的视图组件,比如列表A页面 -->
</router-view>
</keep-alive>

<router-view v-if="!$route.meta.keepAlive && isRouterAlive" >
    <!-- 这里是不被缓存的视图组件,比如详情B页面-->
</router-view>

beforeRouteUpdate 该组件被复用时调用

该组件被复用时调用,可以调用 this 关键字

beforeRouteUpdate (to, from, next) {

// 在当前路由改变,但是该组件被复用时调用

// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,

// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。

// 可以访问组件实例 `this`

beforeRouteLeave 离开组件之后

离开组件之后调用,可以调用 this 关键字

//a 页面  b 页面
//当从a页面 进入 b页面时,
//b页面做了一些操作, 在b页面判断离开时,
//使用组件内守卫,对离开页面事件做一些操作,
beforeRouteLeave(to, from, next){
  if(from.path=='/b'){ //正要离开的时b页面
    // 具体逻辑(不让离去或者怎么样)
  }
  next()
}

应用场景:禁止用户在未保存修改的时候突然离开,可以通过next(false)取消

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

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

结语

本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力。