vue-router 从入门到精通

462 阅读9分钟

前置知识:

什么是路由

所谓路由(routing)就是通过互联网的网络把信息从原地址传输到目的地址的活动(说了,但没完全说)。

后端渲染与后端路由

后端渲染,指的是使用jsp(通过java代码实现从数据库中读取数据,并动态的放在页面中)、php等技术使得需要最终呈现的页面在服务器端实现,然后上传至客户端(区别于ajax)

后端路由,指的是后端处理url和页面之间的映射关系,最后将指定的页面在客户端呈现的过程,后端路由的缺点显而易见,最主要的是不便于前端人员对代码进行维护以及将html代码与数据、逻辑相混杂,不便于编写和维护。

前端路由

前端路由是实现单页面富应用的关键,所谓单页面富应用指的是在静态内容数据库只有一个html文件(区别于后端路由的每个页面一个html文件),而具体展示的内容是通过传入的js文件决定的。每一个显示逻辑(比如展示首页、联系页面)都有一个相对的url(如xxx/home xxx/men),前端路由就是通过改变url来改变展示内容,同时前端路由通过一些方式实现改变url而不刷新页面,从而实现单页面富应用。

改变url但不刷新页面的方式

改变url但不刷新的方式有两个,分别为改变url的hash以及使用html5的history

  • url的hash:改变url的hash值可以做到不刷新而改变页面内容,具体操作方式: location.hash = ‘ (url) ‘
  • html5的history:具体操作方式:history.pushState = ‘ ({},’ ‘ , ‘(url)’) ‘ (将新的url压栈,可以返回) 返回上一个:history.back() 返回下一个: history.forward() history.replaceState =({},”,'(url)’) (将新的url替换旧的url,不可返回) history.go(数值) (返回到 该数值 个url)
  • vue-router中的纯代码实现:使用方法 组件.router.push(url);组件.router.push(‘url’); 组件.router.replace(‘url’)

vue-router安装与使用

通过脚手架生成项目时圈选即可,或者在终端输入命令:npm install vue-router –save 进行安装

Vue在安装插件的时候都需要使用Vue.use(插件)来进行安装

对router文件夹中的index.js文件进行配置

在main.js文件中引入router

至此,路由的底层框架已经搭建完毕了。接下来对vue-router进行配置使用

router文件配置如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

//导入组件
Vue.use(VueRouter)

//安装vue-router
const routes = [
  {
    path:'/',
    // redirect重定向(设定默认首页)
    redirect:'/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

//分配url与组件
const router = new VueRouter({
  routes
})

//生成router
export default router
//导出

最后在app.vue中引入

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/home">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>
//<router-link/>最终渲染出来为超链接(a标签)
//<router-view/>用于站位,即组件位置

url默认为哈希模式,如果想要将url改为history模式(将会去除#),只需要在生成router时添加一个属性

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

router-link的其他属性

  • tag:router-link默认渲染为a标签,通过附加tag属性可以改变router-link渲染的标签,例如tag=”button”
  • relpace:history模式下改变url默认使用pushState,通过添加replace改变url的变化方式,即使用replaceState对url进行更改,直接添加replace即可。
  • active-class:点击router-link后,会自动增加两个类,通过添加active-class属性改变增加类地名称,例如 active-class=’active’;当然,如果要改变所有的router-link可以在路由配置文件(index.js)中生成router处添加属性linkActiveClass:’ ‘

动态路由的使用

我们以生成用户界面为例,现在路由配置文件中进行设置

const routes = [

  {
    path:'/user/:userId',
// 使用语法糖绑定userId
    component:User
  }
]

然后再在App.vue中绑定router-link

<template>
  <div id="app">
    <div id="nav">
      <router-link :to='"/user/"+userId'>user</router-link>
//使用v-bind将data中的userId绑定进入url之中
    </div>
    <router-view/>
  </div>
</template>
<script>
export default ({
  name:'App',
  data(){
    return{
      userId:'bovi'
    }
  }
})
</script>

至此就实现了点击user跳转至user/bovi,同时路由跳转至显示user组件,如果需要实现在user页面中实时显示跳转的userid,则需要在user.vue文件中使用$route

<template>
    <div>
        <h1>我是用户界面</h1>
        <p>{{UserId}}</p>
        <p>{{$route.params.userId}}</p>
// $route指向处于活跃的路由 params:参数(parameters)userId:这里的名称取决于路由配置文件中使用语法糖绑定的属性的名称
    </div>
</template>

<script>
export default({
    name:'user',
    computed:{
        UserId() {
            return this.$route.params.userId;
        }
    }
})
</script>

最终效果图

路由的懒加载

我们在使用构建项目时,会发现最后生成的dist文件中会将css文件和js文件进行分包,其中js文件夹中会有三个文件app..js(当前应用程序开发的所有代码)\manifest..js(给打包的代码做底层支撑)/vendor..js(第三方代码,例如vue/vue-router/axios),但是打包后的文件还是很大(尤其是app.js),如果将所有的路由的对应的js代码放入app.js中,会导致加载缓慢的问题,为了解决这个问题,我们将各个路由对应的js代码单独分离出来,使用懒加载的模式(用到才加载)对整个页面加载进行优化。

路由懒加载实现了将各个路由对应的组件打包成为一个个晓得js代码块,只有在这个路由被访问到是,才加载相应的组件,而不是一次性全部加载。

懒加载的实现方式:(在配置路由的文件中)

const routes = [
  {
    path: '/about',
    name: 'About',


    // which is lazy-loaded when the route is visited.
    component: () => import('../views/About.vue')
  }
]

嵌套路由

嵌套路由非常常见,例如 /home/page1就是一个路由嵌套,一个路由映射一个组件,访问嵌套路由将会将涉及到的组件分别进行访问。实现嵌套路由的步骤很简单:1.创建相对应的子组件,并在路由配置文件中配置对应的子路由;2.在组件内部使用<router-view>标签

//配置路由配置文件
const routes = [
  {
    path: '/home',
    name: 'Home',
    component: () => import('../views/Home.vue'),
//子路由配置,在children数组中进行注册
    children:[
      {
        path:'news',
        component :()=>import('../views/HomeNews')
      }
    ]
  }
]
//子路由对应组件在父路由对应组件中的引入
<template>
  <div class="home">
    <router-link to="/home/news" tag="button">点我康好康的!!</router-link>
    <router-view/>
  </div>
</template>

参数传递

当我们点击路由后进行页面跳转,此时我们希望传输一些数据(例如userId)到新界面并予以使用、呈现时,就需要用到参数传递。

传递参数主要由两种方式:params和query

  • params:配置路由格式:/router/:id 传递方式:在path后面跟上相对应的值(修改app.vue中to的url) 传递后形成的路径:/router/bovi
//路由配置文件
const routes = [
  {
    path:'/user/:userId',
    component:User
  },
]
//app.vue
<template>
  <div id="app">
    <div id="nav">
      <router-link :to='"/user/"+userId'>user</router-link>
    </div>
    <router-view/>
  </div>
</template>
<script>

export default ({
  name:'App',
  data(){
    return{
      userId:'bovi'
    }
  }
})
</script>

//vue.app中传入的部分
<template>
    <div>
        <h1>我是用户界面</h1>
        <p>{{UserId}}</p>
        <p>{{$route.params.userId}}</p>
//两者等价
    </div>
</template>

<script>
export default({
    name:'user',
    computed:{
        UserId() {
            return this.$route.params.userId;
        }
    }
})
</script>

最后呈现结果

  • query:配置路由格式:/router(普通配置格式) 传递方式:在对象中使用query中的key作为传递方式 传递后生成的路径:/router?id=bovi 使用query的原理:通过修改url中的query部分实现路由后缀跳转 url:协议(scheme)://主机(host):端口(port)/路径(path)?查询(query)
//app.vue文件
<template>
  <div id="app">
    <div id="nav">
      <!-- url:协议(scheme)://主机(host):端口(port)/路径(path)?查询(query) -->
      <router-link :to="{path:'/profile',query:{name:'bovi',height:'1.92'}}">profile</router-link>
    </div>
    <router-view/>
  </div>
</template>
<script>
//路由配置文件
const routes = [

  {
    path:'/profile',
    component:() => import('../views/profile.vue')
  }
]
//profile.vue呈现
<template>
    <div>
        <h1>这个是{{$route.query.name}}的档案</h1>
        <h2>他的身高是{{$route.query.height}}米</h2>
    </div>
</template>

当然,跳转路由可以使用万能的方法:注册事件,绑定事件,事件内部为this.router.push()/this.router.push()/this.router.replace(),括号内的东西和:to括号中的东西相同

router与route

方前使用了很多次router以及route,但是这两个东西到底是怎么生成的呢?他们两个又有什么区别呢?

我们在main.js中对router进行打印,不难发现router其实就是路由配置文件中导出的有一个VueRouter实例,查阅源码不难得出这个VueRouter是一个类,其中内置了诸如push()、replace()等函数,同时$router与最后在main.js中传入的router是同一个东西。

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'active'
})

router具体信息

而route就是当前处于激活状态(active)的路由对应的信息

route具体信息

当然,在使用的时候,除了在main.js中不需要加$,其余地方都需要加,因为所有的组件都继承自Vue类的原型

全局导航守卫(router的watch函数)

当我们在路由跳转的时候想要监听并作出相应动作(如实时将网页标题改为当前路由名称)时,就需要用到全局导航守卫,具体使用:在路由配置文件进行事件注册

//路由配置文件
const routes = [

  {
    path:'/profile',
    component:() => import('../views/profile.vue'),
    meta:{
      title:'profile'
    }
//注册元数据(在元数据中在增加路由的其他属性)
  }
]
router.beforeEach((to,from,next)=>{
  //从from到to
  document.title = to.meta.title;
  next();
})

其中,beforeEach函数(前置守卫函数、跳转之前调用)中的参数是一个函数,该函数有三个参数分别是 to、from、next,其中to和from都是一个路由(route),且切换路径是从from到to,而next又是另一个函数,且必须要调用(让整个程序继续进行,),在上述例子中,如遇到路由嵌套,标题将会变为undifined,因为此时的路由to中没有对应的title属性,此时需要使用另一个数组属性matched,他包括了嵌套的所有路由的信息,并从外到内排序,故将document.title= to.meta.title改为document.title = to.matched[0].meta.title,即可

当然在这个场景中也可以调用afterEach函数(后置守卫函数、跳转至后调用),同样的该函数有三个参数,分别是 to、from、next。后置守卫函数不需要主动调用next函数。

以上两个守卫函数成为全局守卫,当然,守卫中还有路由专享守卫(在路由配置中直接定义–const routers =[]中)、组件内守卫(beforRouteEnter()、beforeRouteUpate()、beaforeRouteLeave()),具体内容可查询导航守卫 | Vue Router (vuejs.org)

与keep-alive的邂逅

什么是keep-alive

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

vue的生命周期

当我们切换路由的时候,被切换的路由对应的组件将会被摧毁(destroy),而切换到的路由的组件就会生成(create),而有些组件我们不希望它被重复的摧毁生成,此时需要用到keep-alive标签,只需要将保持不变的内容放入标签内即可。值得一提的是Vue生命周期中的activated(激活)和deactivated(未激活)函数只有在keep-alive中标签中才是有效的(等同于不在keep-alive标签中使用created和destroyed函数)

keep-alive中的重要属性

  • include:字符或者正则表达式,字符间用逗号隔开,只有匹配的组件才会被缓存(匹配name属性)
  • exclude:字符或者正则表达式,字符间用逗号隔开,任何匹配的组件都不会被缓存(匹配name属性)