目录

简介

安装与基本使用

路由器的工作模式

嵌套路由

params传参和query传参

query传参

params传参

命名路由

路由组件传参(props配置)

对象模式

布尔模式

函数模式

router-link的replace属性

编程式路由/导航

缓存路由组件

路由守卫/导航守卫

全局守卫

独享守卫

组件内守卫

路由元信息

完整的导航解析流程


简介

Vue-router是vue的一个插件库,专门用来实现SPA(single page web application,单页web应用)应用;

路由其实就是一组映射关系(key-value),key为路径,value可能是function或者component

安装与基本使用

2022年2月7日以后,vue-router的默认安装版本为版本4,即vue-router4只能用在vue3中,vue-router3才能用在vue2中;

npm install vue-router // 默认安装的是版本4
npm install vue-router@3 // 安装的是版本3

如果要在一个模块化工程中使用它,必须要通过Vue.use()明确的安装路由功能(Vue-router是个插件):

// router/index.js文件 
 
import Vue from 'vue'
import Router from 'vue-router'

// 导入组件 
import Login from '../view/gsys/Login'

// 应用路由插件
Vue.use(Router)

// 创建一个路由器
export default new Router({
    routes: [{
            path: '/login',
            component: Login
        }
    ]
})
// main.js文件
import Vue from 'vue'
import App from './App'
import router from './router'


new Vue({
    el: '#app',
    router,
    components: { App },
    template: '<App/>'
})
// app.vue文件
<div id="app">
<p>
//通过标签router-link组件来导航,to表示导航到的路径,active-class属性实现高亮样式,该标签默认会被渲染成一个<a>标签
    <router-link to="/home" active-class="active">首页</router-link>
</p>

// 路由出口----路由匹配到的组件将会被渲染在此处
<router-view></router-view>

注意:

  • 路由组件通常存放于pages文件夹,一般组件一般存放于components文件夹;
  • 通过切换组件,“隐藏”了的路由组件,默认是被销毁的,所以组件的生命周期函数皆不起作用了,需要的时候再去挂载;
  • 每个组件都有自己的$route属性,里面存储的是自己的路由信息;
  • 整个应用只有一个router,可通过组件的$router属性获取到;
  • 常规参数只会匹配被/分割的URL片段中的字符。若想匹配任意路径,可以使用通配符(*)
{
  // 会匹配所有路径
  path: '*'
}
{
  // 会匹配以 `/user-` 开头的任意路径
  path: '/user-*'
}

 注意使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放到最后。路由{path: '*'}通常用于客户端404错误页

当使用一个通配符时,$route.params内会自动添加一个名为pathMatch参数,它包含了URL通过通配符被匹配到的部分:

// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'

路由器的工作模式

vue-router默认hash模式(url中#及其后面的内容就是hash值)----使用url的hash来模拟一个完整的url,于是当url改变时,页面不会被重新加载;且兼容性较好,但缺点是不美观~

如果不想要很丑的hash,可使用history模式,这种美观,但兼容性较差,更要命的是若后台未配置,则直接访问如http://oursite.com/user/id会返回404;配置history模式代码如下:

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

嵌套路由

配置嵌套路由,使用children配置项:

// 配置路由规则
routes: [{
            path: '/login',
            component: Login
            children: [
                {
                    path: 'user',
                    component: User
                }
            ]
}]
// 跳转

<router-link to="/home/user">用户</router-link>

注意:

配置子路径时,path中不需要加/(因为以/开头的嵌套路径会被当作根路径,可让我们充分的使用嵌套组件而无需设置嵌套的路径),跳转中to的路径要加上!

params传参和query传参

query传参

query传参不会破坏路由规则,只需将参数配置在跳转标签中即可;

// 跳转
// to的字符串写法
<router-link :to="`/home/user?id=${id}&name=${name}`">用户</router-link>
// to的对象写法
<router-link :to="{
    path: '/home/user',
    query: {
        id: id,
        name:name    
    }">
   用户
</router-link>
// 接收参数
$route.query.id
$route.query.name

params传参

params传参要破坏路由配置规则,在path中加入:要传入的参数;

// 配置路由规则
routes: [{
            path: '/login',
            component: Login
            children: [
                {
                    path: 'user/:id/:name',
                    component: User,
                    name: 'user' // 命名路由
                }
            ]
}]
// 跳转
// to的字符串写法
<router-link :to="`/home/user/${id}/${name}`">用户</router-link>
// to的对象写法
<router-link :to="
{
    name: 'user', // 命名路由
    query: {
      id: id,
      name: name
    }
}">用户
</router-link>

并且使用params传参的对象形式时,子页面的router-link不允许使用path了,必须要使用命名路由name。

// 接收参数
$route.params.id
$route.params.name

可看另一篇文章总结params传参与query传参总结

命名路由

设置命名路由后,就可在设置跳转路由地址的地方使用,将长长的path路径替代为name属性:

const router = new VueRouter({
  routes: [
    {
      path: '/user/address',
      name: 'address',
      component: Address
    }
  ]
})

 要链接到一个命名路由,可以改router-link的to属性传一个对象;

// 简化前
<router-link :to="{
    path:'`/user/address?userId=${userId}`',
    query: {userId: 12}
}">
   地址
</router-link>
// 简化后
<router-link :to="{
    name:'address',
    query: {userId: 12}
}">
   地址
</router-link>

路由组件传参(props配置)

在组件中使用$route会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的url上使用,限制了其灵活性,可使用props将组件和路由解耦,有三种方式:

对象模式

若props值是对象,则该对象中所有的key-value的组合最终都会通过props传给组件,这种方式一般用来传递静态参数;

const router = new VueRouter({
  routes: [
    {
      path: '/promotion/from-newsletter',
      component: Promotion,
      props: { id: 12 }
    }
  ]
})

布尔模式

若props值是布尔值,若布尔值为true,则就会将路由收到的所有params参数通过props传给组件(适用于params传参);

函数模式

若props值为函数,则该函数返回的对象中每一组key-value值都会通过props传给组件;

const router = new VueRouter({
  routes: [
    {
      path: '/search',
      component: SearchUser,
      props(route) { 
        return {
            id: route.query.id
        }
    }
  ]
})

三种方式对应组件使用:

<template>
  <div>{{groupId}}</div>
</template>
<script>
export default {
  props: ["id"]
}
</script>

router-link的replace属性

该属性是用来控制路由跳转时操作浏览器历史记录的模式;

浏览器的历史记录有两种写入方式:分别为push和replace,其中push是追加历史记录,replace是替换当前记录,路由跳转时默认为push;

使用如下代码即可开启replace模式,开启了replace模式后,则该页面在浏览器前进后退中则不会出现,即点击浏览器后退按钮后,不会再返回之前的设置了replace属性的页面;

<router-link replace>首页<router-link/>

编程式路由/导航

除了 使用router-link标签创建a标签来定义导航链接,还可借助router的实例方法,通过编写代码来实现。使用$router.push或$router.replace实现路由跳转;

该对象的参数可以是一个字符串路径,或者是一个描述地址的对象;

// 字符串
this.$router.push('home')

// 对象
this.$router.push({ path: 'home' })

// 命名的路由
this.$router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
this.$router.push({ path: 'register', query: { plan: 'private' }})

router.replace同理;

this.$router.forward() // 前进一步
this.$router.back() // 后退
this.$router.go(n) // 可前进(正数),可后退(负数)

缓存路由组件

前面说过,在切换路由组件时,默认会销毁前一个组件,但是有的时候,如果想保留前一页面中的内容,则不希望在前进页面的时候,将该页提交的内容全部删除掉,这种场景下,可通过缓存路由keep-alive来实现;

在路由出口处设置即可:

<keep-alive>
    </router-view>
</keep-alive>

若想仅对某些页面缓存,则可设置include属性,参数值是设置缓存的页面/组件名;

<keep-alive include="home">
    </router-view>
</keep-alive>

缓存的页面多的话,要设置成数组形式:

<keep-alive include="['home','user']">
    </router-view>
</keep-alive>

路由守卫/导航守卫

路由守卫用于对路由进行权限控制,主要分为全局守卫/独享守卫与组件内守卫;

全局守卫

全局守卫分为全局前置守卫/全局解析守卫/全局后置守卫;

  • 全局前置守卫

全局前置守卫router.beforeEach路由初始化时执行,每次路由切换前执行。接收三个参数:

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函数在任何给定的路由守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错;

router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})
  • 全局解析守卫

在版本2.5.0+可以使用router.beforeResolve注册一个全局守卫。这和router.beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由被解析之后,解析守卫就被调用;

  • 全局后置守卫

全局后置守卫router.aftereach用于初始化的时候被调用,每次路由切换之后被调用,与前两个守卫不同的是,它不会接收next函数,并且也不会改变导航本身,因为他已经切换完了;

独享守卫

可在路由配置上直接定义beforeEnter守卫,用于对该路由进行权限控制;参数与全局前置守卫一样;

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

组件内守卫

组件内守卫是指定义在组件内,在路由跳转至组件内部或离开组件的时候的权限控制;有三种beforeRouterEnter/beforeRouterUpdate(2.2新增)/beforeRouteLeave

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

离开守卫通常用于禁止用户在还未保存修改前突然离开,该导航可以通过next(false)来取消;

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

路由元信息

可在需要权限校验的路由配置项上配置meta元信息,如:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

可通过$route.meta.xxx进行访问,也可以通过$route.matched来检查路由记录中的meta字段(一个路由匹配到的所有路由记录都会暴露成$route对象的$route.matched数组)。

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 确保一定要调用 next()
  }
})

完整的导航解析流程

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

前往低代码交流专区

更多推荐