【Vue】Vue router

211 阅读9分钟

路由

路由就是分发请求

路由器就是分发请求的东西

维基百科--路由

  • 路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。

  • 路由引导分组转送,经过一些中间的节点后,到它们最后的目的地。作成硬件的话,则称为路由器Router。

  • 路由表:一个存储到各个目的地的最佳路径的表

  • 路由通常根据路由表来引导分组转送。因此为了有效率的转送分组,创建存储在路由器存储器内的路由表是非常重要的。

hash:#1

默认路由:啥都不写的时候

404路由(保底路由):写错的时候

嵌套路由

路由的hash模式

  • 通过url存路径。只不过用的是url的哈希
  • baidu.com/#1
  • 从window.location.hash获取#号后的,如1
  • 任何情况都可以用hash做前端路由
  • 缺点:SEO不友好
  • 浏览器是不会把#后的内容发给服务器的,服务器收不到哈希

如在浏览器输入baidu.com/#1 其实请求的url:baidu.com

谷歌推出了一款hashbang来解决这个SEO不友好的问题。但是还是没啥用。 数字

  • vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

路由的history模式

  • 通过url存路径。只不过用的是url的路径
  • 直接用路径baidu.com/1
  • 从window.location.pathname获取路径,如/1
  • 只有一种情况下可以使用:后端将所有前端路由都渲染同一个页面(不能是404) + 不支持IE8
  • 后端做到:不管输入baidu.com/1还是baidu.com/2,都进入的是一个页面
  • 缺点:IE8不支持

路由的memory模式

  • 不是url存路径

  • 前端存到localStorage里,手机

  • 从localStorage.getItem('xxx')获取

  • 适合非浏览器,如手机app

  • weex和react native

  • 缺点:不可以分享,因为不是用url,本来url都存了好多信息

$router$route

  1. node_modules/vue-router/types/vue.d.ts里,写了下面代码,表示给Vue添加了这俩属性。因此我们可以在Vue实例/组件里使用this.$routethis.$router
declare module 'vue/types/vue' {
  interface Vue {
    $router: VueRouter
    $route: Route
  }
}
  1. $route获取当前路由信息
  • this.$route.params
  1. $router路由器,用于重定向等
  • this.$router.replace('/404')
  • this.$router.push('/404')

Vue Router常用

import Vue from 'vue'
import VueRouter from 'vue-router'


import Money from '@/views/Money.vue';
import Labels from '@/views/Labels.vue';
import Statistics from '@/views/Statistics.vue';
import NotFound from '@/views/NotFound.vue';
import EditLabel from '@/views/EditLabel.vue';

Vue.use(VueRouter);


//定义路由,每个路由应该映射一个组件。
const routes = [
  {
    path: '/',
    redirect: '/money'   //把根路径重定向到路径/money
  },
  {
    path:'/money',
    component:Money
  },
  {
    path:'/labels',
    component: Labels
  },
  {
    path:'/statistics',
    component: Statistics
  },
  //动态路径参数 以冒号开头
  {
    path: '/labels/edit/:id',
    component: EditLabel
  },
  {
    path:'*',                //输入路径之后,每次都是从最上面的路径依次向下找有没有和输入的路径一样的。如果上面的都不是,那输入的路径就属于*(除去上面后的所有的路径)
    component: NotFound
  }
]

//创建 router 实例,然后传 `routes` 配置
const router = new VueRouter({
  routes
})

export default router
//mian.ts
import router from './router';  //导入一个文件夹,如果这个文件夹有index文件,那就是导入的是index文件里
new Vue({
  router,  //把router传给Vue
  store,
  render: h => h(App)
}).$mount('#app');


 <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/money">Go to Money</router-link>
    <router-link to="/labels">Go to Labels</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>

动态路由匹配

  1. 我们有一个 EditLabel 组件,对于所有标签都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
//动态路径参数 以冒号开头
  {
    path: '/labels/edit/:id',
    component: EditLabel
  },
  1. 一个“路径参数”使用冒号: 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。
  2. 比如模式是/labels/edit/:id,若匹配路径是/labels/edit/1,那么$route.params就是{id:'1'}

Vue Router的history

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

Vue Router的路由对象

一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录 (route records)。

路由对象是不可变 (immutable) 的,每次成功的导航后都会产生一个新的对象。

路由对象出现在多个地方:

  • 在组件内,即 this.$route

  • $route 观察者回调内

  • router.match(location) 的返回值

  • 导航守卫的参数:

router.beforeEach((to, from, next) => {
  // `to` 和 `from` 都是路由对象
})
  • scrollBehavior 方法的参数:
const router = new VueRouter({
  scrollBehavior(to, from, savedPosition) {
    // `to` 和 `from` 都是路由对象
  }
})

路由对象属性

导航守卫

导航守卫是什么

  1. 官方这么说:正如其名,vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

  2. 翻译过来就是:导航守卫就是路由跳转过程中的一些钩子函数

  3. 再直白点:路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机,这就是导航守卫

导航守卫分为三种:全局的、单个路由独享的、组件内

全局的

定义

  1. 是指路由实例上直接操作的钩子函数,他的特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数
  2. 钩子函数按执行顺序包括beforeEach、beforeResolve(2.5+)、afterEach三个

钩子

  1. beforeEach:在路由跳转前触发,参数包括to,from,next三个,这个钩子作用主要是用于登录验证,也就是路由还没跳转提前告知,以免跳转了再通知就为时已晚。
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})
  1. beforeResolve: 在 beforeEach组件内beforeRouteEnter 之后,afterEach之前调用

  2. afterEach:和beforeEach相反,他是在路由跳转完成后触发,参数包括to,from没有了next(参数会单独介绍),他发生在beforeEach和beforeResolve之后,beforeRouteEnter(组件内守卫,后讲)之前。

路由独享的

定义

  1. 是指在单个路由配置的时候也可以设置的钩子函数,其位置就是下面示例中的位置,也就是像Foo这样的组件都存在这样的钩子函数。
  2. 目前他只有一个钩子函数beforeEnter:
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

钩子

beforeEnter :和beforeEach完全相同,在路由跳转前触发,如果他俩都设置则在beforeEach之后紧随执行,参数to、from、next

组件内的

定义

  1. 是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。
  2. 钩子函数按执行顺序包括beforeRouteEnter、beforeRouteUpdate (2.2+)、beforeRouteLeave三个,执行位置如下:
<template>
  ...
</template>

<script>
export default{
  data(){
    //...
  },
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}
<script/>
<style>
  ...
</style>

钩子

  1. beforeRouteEnter :路由进入之前调用,参数包括to,from,next。该钩子在全局守卫beforeEach和独享守卫beforeEnter之后,全局beforeResolve和全局afterEach之前调用
  • 要注意的是该守卫内访问不到组件的实例,也就是this为undefined,也就是他在beforeCreate生命周期前触发。
  • 在这个钩子函数中,可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数,可以在这个守卫中请求服务端获取数据,当成功获取并能进入路由时,调用next并在回调中通过 vm访问组件实例进行赋值等操作,(next中函数的调用在mounted之后:为了确保能对组件实例的完整访问)。
 beforeRouteEnter (to, from, next) {
  // 这里还无法访问到组件实例,this === undefined
  next( vm => {
    // 通过 `vm` 访问组件实例
  })
}
  1. beforeRouteUpdate (v 2.2+): 在当前路由改变时,并且该组件被复用时调用,可以通过this访问实例。参数包括to,from,next。

    可能有的同学会疑问,what is 路由改变 or what is 组件被复用?

  • 对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,组件实例会被复用,该守卫会被调用
  • 当前路由query变更时,该守卫会被调用
  1. beforeRouteLeave:导航离开该组件的对应路由时调用,可以访问组件实例this,参数包括to,from,next。

导航守卫回调参数

  1. to:目标路由对象;

  2. from:即将要离开的路由对象;

  3. next:他是最重要的一个参数,他相当于佛珠的线,把一个一个珠子逐个串起来。以下注意点务必牢记:

  • 但凡涉及到有next参数的钩子,必须调用next() 才能继续往下执行下一个钩子,否则路由跳转等会停止。

  • 如果要中断当前的导航要调用next(false)。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。(主要用于登录验证不通过的处理)

  • 当然next可以这样使用,next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。意思是当前的导航被中断,然后进行一个新的导航。可传递的参数与router.push中选项一致。

  • 在beforeRouteEnter钩子中next((vm)=>{})内接收的回调函数参数为当前组件的实例vm,这个回调函数在生命周期mounted之后调用,也就是,他是所有导航守卫和生命周期函数最后执行的那个钩子。

  • next(error): (v2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

总结

我们最后屡一下顺序:

  1. 当点击切换路由时:beforeRouterLeave-->beforeEach-->beforeEnter-->beforeRouteEnter-->beforeResolve-->afterEach-->beforeCreate-->created-->beforeMount-->mounted-->beforeRouteEnter的next的回调

  2. 当路由更新时:beforeRouteUpdate

参考资料

这个