Vue全家桶之VueRouter

499 阅读8分钟

VueRouter的使用

Vue-Router 的安装和配置

首先,对一个项目而言,我们需要对VueRouter分文件进行统一管理。一般来说,我们会在项目的src目录下创建一个router文件夹,在router文件夹下分别创建两个基本文件,index.js和router.js。

-src
  -router
    index.js //存放router的基本配置
    router.js //存放routes的信息
// router 下的index.js 基本配置
import Vue from 'vue' //引入Vue对象,为了方便注册VueRouter
import VueRouter from 'vue-router' //引入VueRouter,方便new一个新的对象
import routes from './router' // routes是我们配置路由的相关信息,这里也引入进来做初始化

Vue.use(VueRouter) //注册路由

const router = new VueRouter({
  routes  //将routes 的路由配置信息放到创建的路由对象中
})

export default router //导出router对象,该router对象会在main.js中被赋值到vue实例中去
// router 下的 router.js基本配置信息
const Home = () => import('@/views/Home') //路由懒加载
const About = () => import('@/views/About') //路由懒加载
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  }
]
export default routes //导出 routes对象,在index.js里面 引入

Vue-Router的路由懒加载

路由懒加载的目的是为了减少首次进入页面的请求时间,使用户看到首页速度更快。

路由懒加载会根据你的设置将打包的js文件分割成若干个小js,按照路由跳转的需求按需加载,跳转到哪个路由地址,就加载哪个地址的js文件。

// 路由懒加载的使用介绍
// router.js文件中
const Home = () => import('@/views/Home') //路由懒加载
const About = () => import('@/views/About') //路由懒加载
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  }
]
export default routes //导出 routes对象,在index.js里面 引入

Vue-Router的嵌套路由

嵌套路由就是在一个被路由过来的页面下可以继续使用路由,嵌套也就是路由中的路由的意思。

比如在vue中,我们如果不使用嵌套路由,那么只有一个<router-view>,但是如果使用,那么在一个组件中就还有<router-view>,这也就构成了嵌套。

// router.js文件
const Home = () => import('@/views/Home') //路由懒加载
const About = () => import('@/views/About')
const Parent = () => import('@/views/Parent')
const Child = () => import('@/views/Child')
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
// 这里要注意,children后面的path 一定不要加/,不然还是会解析为根目录的路由
  { 
    path: '/parent',
    name: 'Parent',
    component: Parent,
    children: [      
      {
        path: 'child',
        component: Child
      }
    ]
  }
]
export default routes

Vue-Router的命名路由

给一个路由命一个唯一的名称,主要用在标签<router-link>中,可以通过标签的to属性跳转到指定命名路由。在后续的路由守护、重定向等里面也都可以用到命名路由。

// router.js文件
const Home = () => import('@/views/Home') //路由懒加载
const About = () => import('@/views/About')
const Parent = () => import('@/views/Parent')
const Child = () => import('@/views/Child')
const routes = [
  {
    path: '/',
    name: 'Home', //这种就是命名路由
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
// 这里要注意,children后面的path 一定不要加/,不然还是会解析为根目录的路由
  { 
    path: '/parent',
    name: 'Parent',
    component: Parent,
    children: [      
      {
        path: 'child',
        name: 'child',
        component: Child
      }
    ]
  }
]
export default routes
// 在parent.vue文件中
// 通过router-link 标签中的to属性和v-bind的绑定使用,我们可以通过命名路由来实现跳转,而不需要传path路径
<template>
  <div>
      this is parent
      <router-link :to="{name: 'child'}">child</router-link>
      <router-view></router-view>
  </div>
</template>

Vue-Router命名视图

命名视图一般使用在需要多个路由视图中,例如一个vue页面中需要展示三个不同的<router-view>这时候就必须使用命名视图来达成效果,一般来说命名视图用来做布局用

// router.js文件
const Home = () => import('@/views/Home')
const About = () => import('@/views/About')
const Parent = () => import('@/views/Parent')
const Header = () => import('@/views/Header')
const Right = () => import('@/views/Right')
const Left = () => import('@/views/Left')

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
// 在parent页面中通过children子路由实现命名视图
  {
    path: '/parent',
    name: 'Parent',
    component: Parent,
    children: [
      {
        path: '',
        components: {
          default: Header,
          left: Left,
          right: Right
        }
      }
    ]
  }
]
export default routes
// 在parent.vue中
<template>
  <div>
      this is parent
      <router-view></router-view>
      <router-view name="left"></router-view>
      <router-view name="right"></router-view>
  </div>
</template>

Vue-Router 重定向

重定向是重新设置当前路由功能,比如我们可以讲/重定向到/home以防止用户手动通过/来访问,可以让这两个路由目标地址保持一致

// router.js文件
const Home = () => import('@/views/Home')
const About = () => import('@/views/About')
const Parent = () => import('@/views/Parent')
const Header = () => import('@/views/Header')
const Right = () => import('@/views/Right')
const Left = () => import('@/views/Left')

const routes = [
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
// 这三种写法都可以实现redirect重定向,一般来说redirect是为了防止用户通过修改url路径直接访问路径
  {
    path: '/',
    // redirect: { name: 'Home' }
    // redirect: '/home'
    redirect: to => {
      return { name: 'Home' }
    }
  },
  {
    path: '/about',
    name: 'About',
    component: About,
    redirect: to => {
      return { name: 'Home' }
    }
  }
]
export default routes

Vue-Router别名

给路由取别名可以更加方便的访问路由,不需要受限于繁杂的路由嵌套地址,一个别名可以顶替冗长的地址,使用起来更加方便。

// router.js文件
const Home = () => import('@/views/Home')
const About = () => import('@/views/About')
const Parent = () => import('@/views/Parent')
const Header = () => import('@/views/Header')
const Right = () => import('@/views/Right')
const Left = () => import('@/views/Left')
//这里的alias就是别名,如果path的路径长度很长,这时候别名看起来就比较清爽了
const routes = [
  {
    path: '/home',
    name: 'Home',
    component: Home,
    alias: '/winter'
  }
]
export default routes

Vue-Router的编程式导航

在.vue中可能需要用户通过连接手动的跳转页面,这时候就需要编程式导航的方式来实现手动页面跳转,其中push是跳转到指定的路径,back是返回上一次的路径,forward是前进到返回之前的路径,replace是替换当前路径,注意replace不会留下跳转记录,因此无法通过backforward返回,而push会留下记录。

// 编程式导航
<template>
  <div>
      this is parent
      <button @click="handleClick('push')">跳转到首页</button>
      <button @click="handleClick('back')">后退</button>
      <button @click="handleClick('forward')">前进</button>
      <button @click="handleClick('replace')">替换当前路径</button>
  </div>
</template>
<script>
export default {
  name: 'Parent',
  methods: {
    handleClick (type) {
      switch (type) {
        case 'push' :
          this.$router.push('/')
          break
        case 'back' :
          this.$router.back()
          break
        case 'forward':
          this.$router.forward()
          break
        case 'replace':
          this.$router.replace('/about')
          break
        default:
          break
      }
    }
  }
}
</script>

编程式导航我们可以传params或者query给目标路由,传query的时候,会在url最后的位置加上?name=thisisquery类似这种内容,目前路由可以通过$route.query.name来获取传递的信息。如果传递的是params,一定要注意pathparams不能一起用,path会导致params失效。params可以通过$route.params.name来获取

// 传query
<script>
export default {
  name: 'Home',
  methods: {
    handleClick () {
      console.log(this.$route)
      this.$router.push({
        // name: 'Home'
        path: '/home',
        query: {
          name: 'thisisquery'
        }
      })
    }
  }
}
</script>
// 传params
// params不能与path一起使用,path会导致params失效
<script>
export default {
  name: 'Home',
  methods: {
    handleClick () {
      console.log(this.$route)
      this.$router.push({
        name: 'Home',
        params: {
          name: 'woshilisi'
        }
      })
    }
  }
}
</script>

Vue-Router组件传参

路由也可以使用组件的props传参,只要设置props: true属性即可,需要在组件使用props属性接受路由传递的参数,在路由设置里面还有其他两种设置props方式,分别是对象模式、函数模式。

// 路由的props三种方式
// 布尔值方式
// router.js文件中的route配置里面
// 一定要注意path后面要加上:属性名
  {
    path: '/about/:name',
    name: 'About',
    component: About,
    props: true
  }

//对象模式 要注意对象模式的path中没有:name
  {
    path: '/about',
    name: 'About',
    component: About,
    // props: true
    props: {
      name: 'morenzhi'
    }
  }

//函数模式 要注意函数模式中path没有:name,并且函数模式只能通过query传值
  {
    path: '/about',
    name: 'About',
    component: About,
    props: (route) => ({
      name: route.query.name
    })
  }

// 在.vue文件中
export default {
  name: 'About',
  props: {
    name: {
      type: String,
      default () {
        return '没有值'
      }
    }
  }
}


Vue-Router HTML5 History模式

History模式需要浏览器的支持,VueRouter的默认模式是hash,在url中有#存在,实际项目中一般用History模式,但是需要注意,History需要后端支持,因为一旦url路径不匹配很容易就报错,因此需要后端支持。

// 路由index.js文件中
export default new Router({
		mode: 'history',
		routes
})

Vue-Router 导航守卫

VueRouter中的导航守卫,本质上就是钩子函数。它可以在实现跳转的时候做一个相关操作,比如导航拦截,重定向到登录页面等等操作,导航守卫相关的总共七个函数,分布在:路由本身,某个具体路由内部,组建内部。

// 导航守卫
// index.js 文件
// to 是失效页面的url
// from 激活页面的url
// next 执行下一步,如果不调用next,本次页面跳转不执行,必须调用next
router.beforeEach((to, from, next) => {
  console.log('to:')
  console.log(to)
  console.log('from:')
  console.log(from)
  next()
})

//后置导航钩子
router.afterEach((to, from) => {
  console.log('后置导航钩子')
})

//全局解析守卫
router.beforeResolve((to, from, next) => {
  console.log('全局解析守卫')
  next()
})

// 路由独享守卫
// router.js中
  {
    path: '/about',
    name: 'About',
    component: About,
    props: (route) => ({
      name: route.query.name
    }),
// 一般在这里可以判断目标从哪个路由过来,防止用户手动通过输入路由地址跳转
    beforeEnter: (to, from, next) => {
      console.log('路由独享守卫')
      if (from.name === 'Home') {
        next()
      } else {
        next('/')
      }
    }
  }

// 组件内的路由守卫
// 要注意,在beforeRouteEnter里面无法使用this获得当前组件对象,因为此时当前组件还没有渲染成功
<script>
export default {
  name: 'About',
  props: {
    name: {
      type: String,
      default () {
        return '没有值'
      }
    }
  },
  beforeRouteEnter (to, from, next) {
    console.log('组件内导航守卫')
    next(vm => {
      console.log(vm.name)
    })
  },
//一般来说,beforeRouteLeave 可以用来做确认离开信息,可以通过next(false)取消当前页面跳转
  beforeRouteLeave (to, from, next) {
    console.log('组件内离开导航守卫')
    next()
	},
// 组件没有被销毁,当前路由更新的时候会进入
  beforeRouteUpdate (to, from, next) {
    console.log('组件内更新守卫')
    next()
  }
}
</script>

完成的流程:

​ 1: 导航被触发

​ 2:在失活组件中调用离开守卫 beforeRouteLeave

​ 3:调用全局的前置守卫beforeEach

​ 4:在复用的组件里调用 beforeRouteUpdate

​ 5:调用路由独享守卫beforeEnter

​ 6:解析异步路由组件

​ 7:在被激活的组件里调用beforeRouteEnter

​ 8:调用全局解析守卫beforeResolve

​ 9:导航被确认

​ 10:调用全局的afterEach后置勾子

​ 11:触发DOM更新

​ 12:用创建好的实例调用beforeRouterEnter守卫里面的next回调函数

Vue-Router路由元信息

//router.js文件中的代码
{
    path:"/test",
    name:"test",
    component:()=>import("@/components/test"),
    meta:{
        title:"测试页面", //配置title
        keepAlive: true //是否缓存
    }
}
//index.js中的代码 设置title
router.beforeEach((to,from,next)=>{
    if(to.meta.title){
        document.title=to.meta.title
    }
    next()
})

<!-- app.vue中的代码 -->
<!-- 需要被缓存的路由入口 -->
<keep-alive>  
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>

<!-- 不需要被缓存的路由入口 -->
<router-view v-if="!$route.meta.keepAlive"></router-view>