Vue-Router
摘要
vue-router,顾名思义,就是用来处理 Vue 中路由的工具。在此献上官网
# 介绍
什么是路由?其实路由可以简单地理解为:路由就是根据网址的不同,返回不同的内容给用户
在开发 Vue 项目的时候,就是使用这个工具来进行项目的路由管理
# 多页面应用 VS 单页面应用
提及路由,就会想到多页面和单页面的问题,这里稍微了解一下
多页面应用
- 特点:页面跳转是直接返回 HTML 文件
- 优点:首屏时间快,SEO 效果好
- 缺点:页面切换慢
单页面应用
- 特点:使用 JS 监听路由来动态渲染页面
- 优点:页面切换快
- 缺点:首屏时间稍慢,SEO 差
- 解决方案:使用 vue-ssr 服务端渲染来解决首页渲染慢的问题
# 目录
在已经搭建完 Vue-CLI 的情况下再继续新建文件,目录结构如下
├─src
|  ├─App.vue
|  ├─main.js
|  ├─router
|  |   └index.js
|  ├─components
|  |     ├─A.vue
|  |     ├─B.vue
|  |     └HelloWorld.vue
|  ├─assets
|  |   └logo.png
# 使用方法
# 引入 vue-router
由于在搭建 vue-cli 中已经安装好了 vue-router,所以就不用再多做配置。
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
# 引入定义路由
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import A from '@/components/A'
import B from '@/components/B'
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B
    }
  ]
})
# 修改 .vue 文件
App.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>
<style>
</style>
HelloWorld.vue
这里的 A.vue 和 B.vue 只需要简单的模板即可
<template>
  <div>
    <router-link to='/A'>toA</router-link>
    <router-link to='/B/1' tag='p'>toB</router-link>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld'
}
</script>
<style scoped>
</style>
在 .vue 文件中
- 使用<router-view/>来使路由生效,显示当前路由地址所对应的内容
- 使用<router-link>代替 a 标签进行页面跳转。但是<router-link>可以使用 tag 属性变成其他标签
# 基本参数讲解
在 router/index.js 中
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import A from '@/components/A'
import B from '@/components/B'
Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B
    }
  ]
})
- routes 数组,可以对每个路由进行配置
- path 可以用来指定路由地址,可以用 :来定义一个变量名,从而使用param路由,在组件内可以使用this.$route来查看。
- name 开发者可以自己定义,在编程式导航中需要用到
- component 则是路由对应的组件
 
- path 可以用来指定路由地址,可以用 
还有更多的参数可以参考这里
# 动态路由
对于参数式如何传递的,vue-router 提供了一种动态路由的方式,如果你够细心的话,就会发现,router/index.js 中对于 B 组件的路由如此设置的 —— 使用 : 来绑定传递的参数
{
  path: '/B/:id',
  name: 'B',
  component: B
}
那么如何获取传递的参数?
B.vue
<template>
  <div>
    B{{ id }}
  </div>
</template>
<script>
export default {
  name: 'B',
  data () {
    return {
      id: this.$route.params.id  // 获得传递的参数
    }
  }
}
</script>
<style scoped>
</style>
# 编程式导航
仅仅使用动态路由来进行参数传递其实并不满足于开发的需求,所以就有编程式导航的出现
如何使用编程式导航,可以查看以下示例
A.vue
<template>
  <div>
    <p>A</p>
  <div>
    <button @click='handleClick'>Click to B</button>
    <button @click='handleClickBack'>Click to Back</button>
  </div>
  </div>
</template>
<script>
export default {
  name: 'A',
  methods: {
    handleClick () {
      // this.$router.push('/B/5')
      this.$router.push({
        name: 'B',
        params: {
          id: 6
        }
      })
    },
    handleClickBack () {
      this.$router.go(-1)
    }
  }
}
</script>
<style scoped>
</style>
# 细节点优化
# 去除路由中的哈希值
- vue-router默认- hash模式 —— 使用 URL 的- hash来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
- 如果不想要很丑的 hash,我们可以用路由的history模式,
- 在 router/index.js的new Router对象里配置一个mode:'history'来去除路由中的哈希值
- 因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问就会返回 404,所以需要后端进行相对应的配置,请点击这里进行查看
# 过渡动效
<router-view> 是基本的动态组件,所以我们可以用 <transition> 组件给它添加一些过渡效果:
App.vue
<template>
  <div id="app">
    <transition>
      <router-view/>
    </transition>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>
<style>
.v-enter,
.v-leave-to{
  opacity: 0;
}
.v-enter-active,
.v-leave-active{
  transition: 1s opacity;
}
</style>
# 路由懒加载
当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
const A = () => import('@/components/A')
const B = () => import('@/components/B')
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B
    }
  ]
})
# 404 优化
多定义一个 notFound 的组件
notFound.vue
<template>
  <div>
    404
  </div>
</template>
<script>
export default {
  name: 'notFound'
}
</script>
<style scoped>
</style>
然后再 router/index.js 中进行配置
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
const A = () => import('@/components/A')
const B = () => import('@/components/B')
const notFound = () => import('@/components/notFound')
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B
    },
    {
      path: '/*',  //这里会把不存在的路由全部跳转到这个组件中
      name: 'notFound',
      component: notFound
    }
  ]
})
# 路由解耦
在获取动态路由的参数的时候,使用 this.$route.params.id 来获取参数的,但是这样耦合性不好,vue-router 提供了一种 props 的方式来传递参数。请看以下示例
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
const A = () => import('@/components/A')
const B = () => import('@/components/B')
const notFound = () => import('@/components/notFound')
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B,
      props: true
    },
    {
      path: '/*',
      name: 'notFound',
      component: notFound
    }
  ]
})
B.vue
<template>
  <div>
    B{{ id }}
  </div>
</template>
<script>
export default {
  name: 'B',
  props: ['id']
  // data () {
  //   return {
  //     id: this.$route.params.id
  //   }
  // }
}
</script>
<style scoped>
</style>
# 滚动行为
使用 scrollBehavior 参数,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
const A = () => import('@/components/A')
const B = () => import('@/components/B')
const notFound = () => import('@/components/notFound')
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/A',
      name: 'A',
      component: A
    },
    {
      path: '/B/:id',
      name: 'B',
      component: B,
      props: true
    },
    {
      path: '/*',
      name: 'notFound',
      component: notFound
    }
  ],
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      // return 期望滚动到哪个的位置
      return { x: 0, y: 0 }
    }
  }
})
# 结语
关于 Vue-Router 的总结就到这里,其实并不是很详细,而且还有嵌套路由和导航守卫者两个方面没有涉及到,但这是我个人觉得开发中比较实用的部分,就在此做下笔记,进行学习总结,更多有需要的部分可以到 Vue-Router 的官网上进行查看
