Vue Router

一、什么是路由

路由概述

路由(route)其实是一种映射关系,类似于key===>value的键值对的关系,其中key表示请求的路径path。

路由是根据不同的 url 地址展示不同的内容或页面;

路由分为前端路由和后端路由

​ 前端路由:前端路由的value表示组件,一个path映射一个组件;

​ 后端路由:后端路由的value表示处理请求的回调函数,针对不同请求的 path,处理不同的业务逻辑

前端路由

前端路由很重要的一点是页面不刷新,前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,每跳转到不同的URL都是使用前端的锚点路由.

随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载。

优点:

​ 1.用户体验好,和后端网速没有关系,不需要每次都从服务器全部获取,快速展现给用户

​ 2.可以在浏览器中输入指定想要访问的url路径地址。

​ 3.实现了前后端的分离,方便开发。有很多框架都带有路由功能模块。

缺点:

​ 1.使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存

​ 2.单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

后端路由

​ 浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示, 返回不同的页面,

​ 意味着浏览器会刷新页面,网速慢的话说不定屏幕全白再有新内容。

优点:

​ 分担了前端的压力,html和数据的拼接都是由服务器完成。

缺点:

​ 当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问。另外一个就是如果当前网速过慢,那将会延迟页面的加载,对用户体验不是很友好。

组件和路由

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。

组件是对特点功能代码(html,css,js)的封装, 通过组件的名字可以重复利用该组件中的代码。

路由是负责将进入的浏览器请求映射到特定的 组件 代码中。 即决定了由谁(组件)去响应客户端请求。简单说路由就是url地址和对应的资源(组件)的映射,通过一个路径的url地址,可以唯一找到一个资源。路由不包含在vue中,是一个插件,需要单独下载。

组件我们一般定义在src/components下面;路由我们一般定义在src/views或src/pages下面;

区分路由组件和非路由组件的最大区别在于:组件有没有被注册为路由!

二、Vue Router概述

github: https://github.com/vuejs/vue-router

中文文档: http://router.vuejs.org/zh-cn/

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 history模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

Vue Router的三个基本概念

route:首先它是个单数,译为路由,即我们可以理解为单个路由或者某一个路由;当前激活的路由信息对象,可通过this.$route获取。route对象是只读的,里面的属性是不可变的,不过可以使用watch (侦听器)监测它的变化。

routes:它是个复数,表示多个的集合才能为复数;即我们可以理解为多个路由的集合,JS中表示多种不同状态的集合的形式只有数组和对象两种,事实上官方定义routes是一个数组;所以我们记住了,routes表示多个路由的集合;

router:译为路由器,上面都是路由,这个是路由器,我们可以理解为一个容器包含上述两个或者说它是一个管理者,负责管理上述两个;举个常见的场景的例子:当用户在页面上点击按钮的时候,这个时候router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由;它是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性

Vue Router实现原理

SPA(single page application):单一页面应用程序,只有一个完整的页面。它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是:更新视图而不重新请求页面。Vue Router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式。Vue Router中默认使用的是hash模式。

hash模式

随着 AJAX 的流行,异步数据请求交互运行在不刷新浏览器的情况下进行。而异步交互体验的更高级版本就是 SPA —— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。但是这样存在一个问题,就是 url 每次变化的时候,都会造成页面的刷新。那解决问题的思路便是在改变url的情况下,保证页面的不刷新。在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于:localhost:8080/#/login

这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作:

function matchAndUpdate () {
   // todo 匹配 hash 做 dom 更新操作
}
window.addEventListener('hashchange', matchAndUpdate)

history 模式

因为HTML5标准发布,多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有popstate事件。通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了HTML5的实现,单页路由的url就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。

function matchAndUpdate () {
   // todo 匹配路径 做 dom 更新操作
}

window.addEventListener('popstate', matchAndUpdate)

相关API

1、VueRouter(): 用于创建路由器的构建函数

new VueRouter({ 
    // 多个配置项 
})

2、路由配置

routes: [
    { 
        // 一般路由 
        path: '/about', 
        component: About 
    },
    { // 自动跳转路由 
        path: '/', 
        redirect: '/about' 
    }
]

3、注册路由器

import router from './router

new Vue({ 
    router 
})

4、使用路由组件标签

1. <router-link>: 用来生成路由链接
	<router-link to="/xxx">Go to XXX</router-link>

2. <router-view>: 用来显示当前路由组件界面
    <router-view></router-view>

三、安装

直接下载 / CDN

https://unpkg.com/vue-router/dist/vue-router.js

Unpkg.com 提供了基于 NPM 的 CDN 链接。上面的链接会一直指向在 NPM 发布的最新版本。你也可以像 https://unpkg.com/vue-router@2.0.0/dist/vue-router.js 这样指定 版本号 或者 Tag。

在 Vue 后面加载 vue-router,它会自动安装的:

<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>

NPM

npm install vue-router

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

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

Vue.use(VueRouter)

如果使用全局的 script 标签,则无须如此 (手动安装)。

构建开发版

如果你想使用最新的开发版,就得从 GitHub 上直接 clone,然后自己 build 一个 vue-router

git clone https://github.com/vuejs/vue-router.git node_modules/vue-router

cd node_modules/vue-router

npm install

npm run build

四、基本路由

用 Vue.js + Vue Router 创建单页应用,是非常简单的。使用 Vue.js 已经可以通过组合组件来组成应用程序,当把 Vue Router 添加进来后,需要做的是将组件 (components) 映射到路由 (routes),然后告诉 Vue Router 在哪里渲染它们。

基本案例

基于vue-cli构建的模版项目来实现;

目录结构

src
	|- views
		|- page1.vue
		|- page2.vue
	|- router
		|index.js
	main.js
	App.vue

定义路由组件

page1.vue:

<template>
<div>
    <h1>page1</h1>
    <p>{{msg}}</p>
</div>
</template>
<script>
    export default {
        data () {
            return {
                msg: "我是page1组件"
            }
        }
    }
</script>

<style >

</style>

page2.vue:

<template>
<div>
    <h1>page2</h1>
    <p>{{ msg }}</p>
</div>
</template>
<script>
    export default {
        data() {
            return {
                msg: "我是page2组件",
            };
        },
    };
</script>
<style >
</style>

路由器模块: src/router/index.js

// 引入vue
import Vue from 'vue'
// 引入vue-router
import VueRouter from 'vue-router'
// 第三方库需要use一下才能用
Vue.use(VueRouter)
// 引用page1页面
import page1 from '../views/page1.vue'
// 引用page2页面
import page2 from '../views/page2.vue'

// 定义routes路由的集合,数组类型
const routes = [
  // 单个路由均为对象类型,path代表的是路径,component代表组件
  {
    path: '/page1',
    component: page1
  },
  {
    path: "/page2",
    component: page2
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

注册路由器: main.js

import Vue from 'vue'
import App from './App'
// 引用router.js
import router from './router/index.js'
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  // 一定要注入到vue的实例对象上
  router,
  components: {
    App
  },
  template: '<App/>'
})

应用组件: App.vue

<template>
<div id="app">
    <img src="./assets/logo.png" />
    <div>
        <!--router-link定义路由链接-->  
        <router-link to="/page1">Page1</router-link>
        <router-link to="/page2">Page2</router-link>
    </div>
    <!--router-view用于渲染当前路由组件-->
    <router-view></router-view>
</div>
</template>

<script>
    export default {
        name: "App",
    };
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

就这样,我们的页面就可以进行路由跳转和切换了,路由的基本使用就完成了;但是有个问题就是我们第一次进去是看不到路由页面的,这是因为我们没有设置默认值,我们首次进入的时候路径是为空的,那么我们可以这么解决:

修改src/router/index.js:

// 定义routes路由的集合,数组类型
const routes = [
    // 单个路由均为对象类型,path代表的是路径,component代表组件
    {
        path: '/page1',
        component: page1
    },
    {
        path: "/page2",
        component: page2
    },
    // 可以配置重定向
    {
        path: '',
        redirect: "/page1"
    },
    // 或者重新写个路径为空的路由
    /* {
    path: "",
    component: page1
  } */
]

上面的两种解决方案都是可以解决的,配置重定向的意思就是当匹配到路径为空的时候,就会重定向到page1,执行page1的路由;或者我们也可以重新配置个路由,路径为空的时候router-view展示page1的页面;

用重定向和单独配置路由的区别:

重定向实际上是当匹配到路径符合条件的时候去执行对应的路由,当然这个时候的url上面的地址显示的是对应的路由,页面也是对应的路由页面;

重新配置路由是当匹配到路径符合条件的时候,router-view页面展示部分负责拿符合条件路由的页面来展示,实际上url是没有发生变化的;

优化路由配置

// 在index.html引入样式
<style>
    .router-active {
        color: red
    }

</style>

在实例化路由器的时候,设定linkActiveClass: ‘router-active’, // 指定选中的路由链接的 class

const router = new VueRouter({
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
});

应用案例

定义路由组件

Home.vue

<template>
  <div>
    <h2>我是主页</h2>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

About.vue

<template>
  <div>
    <h2>关于我们页面</h2>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

路由器模块

/*
路由器对象模块
 */
import Vue from 'vue'
import VueRouter from 'vue-router'

import About from '../pages/About.vue'
import Home from '../pages/Home.vue'

// 声明使用vue-router插件
/*
内部定义并注册了2个组件标签(router-link/router-view),
给组件对象添加了2个属性:
  1. $router: 路由器
  2. $route: 当前路由
 */
Vue.use(VueRouter)


// 定义routes路由的集合,数组类型
const routes = [{
    path: '/about',
    component: About
  },
  {
    path: '/home',
    component: Home,

  },
  // 路由重定向
  {
    path: '',
    redirect: '/home'
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

注册路由器

/*
入口JS
 */
import Vue from 'vue'
import App from './App.vue'
import router from './router'


/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: {
    App
  }, // 映射组件标签
  template: '<App/>', // 指定需要渲染到页面的模板
  router // 注册路由器
})

应用组件

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Router Test</h2></div>
      </div>
    </div>

    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!--生成路由链接-->
          <router-link to="/home" class="list-group-item">Home</router-link>
          <router-link to="/about" class="list-group-item">About</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!--显示当前组件-->
            <keep-alive>
              <router-view></router-view>
            </keep-alive>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

总结

编写使用路由的 4 步 :

​ 1) 定义路由组件

​ 1) 配置路由

​ 3) 注册路由

​ 4) 使用路由

<router-link>

<router-view>

那么还有些复杂情况,是基本路由实现不了的;我们来接着往下看

五、嵌套路由

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:

/user/list                            /user/add
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | UserList     | |                  | | UserAdd     | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。官方文档中给我们提供了一个children属性,这个属性是一个数组类型,里面实际放着一组路由;这个时候父子关系结构就出来了,所以children属性里面的是路由相对来说是children属性外部路由的子路由;

基本案例

1、首先在我们的src/pages目录下新建两个vue文件,分别是phone.vue和computer.vue

phone.vue

<template>
<div>
    <p>{{ msg }}</p>
</div>
</template>
<script>
    export default {
        data() {
            return {
                msg: "嵌套手机组件",
            };
        },
    }
</script>
<style>
</style>

computer.vue

<template>
<div>
    <p>{{ msg }}</p>
</div>
</template>
<script>
    export default {
        data() {
            return {
                msg: "嵌套电脑组件",
            };
        },
    }
</script>
<style>

</style>

2、我们来修改src/router/index.js

import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter)
import page1 from '../views/page1.vue';
import page2 from '../views/page2.vue';
import user from '../views/user.vue';
import phone from '../views/phone.vue';
import computer from '../views/computer.vue'

const routes = [{
    path: '/page1',
    component: page1,
    // 嵌套路由
    children: [{
        path: "phone",
        component: phone
      },
      {
        path: "computer",
        component: computer
      },
    ]
  },
  {
    path: "/page2",
    component: page2
  },
  {
    path: '',
    redirect: "page1"
  },
  // {path:"",component:page1},
  {
    path: "/user/:name",
    component: user
  }

]

const router = new VueRouter({
  routes
});

export default router

3、然后我们再修改我们的page1.vue文件:

当我们访问page1组件的时候,要展示phone和computer;

<template>
<div>
    <h1>page1</h1>
    <p>{{ msg }}</p>
    <div>
        <router-link to="/page1/phone">phone</router-link>
        <router-link to="/page1/computer">computer</router-link>
    </div>
    <!-- 必不可少 -->
    <router-view></router-view>
</div>
</template>
<script>
    export default {
        data() {
            return {
                msg: "我是page1组件",
            };
        },
    };
</script>

<style >
</style>

上面说到了,children属性其实就是一个子路由集合,数组结构里面放着子路由;

在这里插入图片描述

应用案例

在这里插入图片描述

定义路由组件

Home组件

该组件下面有两个子组件,分别是news和messgae;

<template>
  <div>
    <h2>我是主页</h2>
    <div>
      <ul class="nav nav-tabs">
        <li><router-link to="/home/news">新闻中心</router-link></li>
        <li><router-link to="/home/message">消息中心</router-link></li>
      </ul>

      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>
News组件
<template>
  <ul>
    <li v-for="(news, index) in newsArr" :key="index">{{news}}</li>
  </ul>
</template>

<script>
  export default {
    data () {
      return {
        newsArr: ['News001', 'News002', 'News003']
      }
    }
  }
</script>

<style>

</style>
Message组件
<template>
  <div>
    <ul>
      <li v-for="m in messages" :key="m.id">
        <a href="?">{{ m.title }}</a>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messages: [
        /* {id: 1, title: 'Message001'},
          {id: 3, title: 'Message003'},
          {id: 5, title: 'Message005'}*/
      ],
    };
  },

  mounted() {
    setTimeout(() => {
      const messages = [
        { id: 1, title: "Message001" },
        { id: 3, title: "Message003" },
        { id: 5, title: "Message005" },
      ];
      this.messages = messages;
    }, 1000);
  },
};
</script>

<style>
</style>

路由器模块: src/router/index.js

/*
路由器对象模块
 */
import Vue from 'vue'
import VueRouter from 'vue-router'

import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'

// 声明使用vue-router插件
Vue.use(VueRouter)


// 定义routes路由的集合,数组类型
const routes = [{
    path: '/about',
    component: About
  },
  {
    path: '/home',
    component: Home,
    children: [{
        path: '/home/news',
        component: News
      },
      {
        path: 'message',
        component: Message,
      },
      {
        path: '',
        redirect: '/home/news'
      }
    ]
  },
  {
    path: '',
    redirect: '/home'
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

注册路由器: main.js

/*
入口JS
 */
import Vue from 'vue'
import App from './App.vue'
import router from './router'


/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: {
    App
  }, // 映射组件标签
  template: '<App/>', // 指定需要渲染到页面的模板
  router // 注册路由器
})

应用组件

这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 Home组件的模板添加一个 <router-view>

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Router Test</h2></div>
      </div>
    </div>

    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!--生成路由链接-->
          <router-link to="/home" class="list-group-item">Home</router-link>
          <router-link to="/about" class="list-group-item">About</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!--显示当前组件-->
            <keep-alive>
              <router-view></router-view>
            </keep-alive>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径

你会发现,children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由。

此时,基于上面的配置,当你访问 /user 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个空的子路由:

const router = new VueRouter({
  routes: [
    {
      path: '/user', component: User,
      children: [
        // 当 /user 匹配成功,UserHome 会被渲染在 User 的 <router-view> 中
        { path: '', component: UserHome },
        // ...其他子路由
      ]
    }
  ]
})

六、动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”来达到这个效果:当我们在地址后面直接添加任意字符,我们会发现文档内容随着我们的更改而改变。

基本案例 - 动态路径参数

点击Message里面的链接,可以显示对应的详情数据;

Message.vue组件

<template>
  <div>
    <ul>
      <li v-for="m in messages" :key="m.id">
        <router-link :to="`/home/message/detail/${m.id}`">{{
          m.title
        }}</router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messages: [],
    };
  },

  mounted() {
    setTimeout(() => {
      const messages = [
        { id: 1, title: "Message001" },
        { id: 3, title: "Message003" },
        { id: 5, title: "Message005" },
      ];
      this.messages = messages;
    }, 1000);
  },
};
</script>

<style>
</style>

MessageDetail.vue组件:

<template>
  <ul>
    <li>id: {{ $route.params.id }}</li>
    <li>title: {{ detail.title }}</li>
    <li>content: {{ detail.content }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      messageDetails: [],
      detail: {},
    };
  },
  mounted() {
    // 改变当前路由组件参数数据时, 不会重新创建组件对象, mounted不会重新执行
    setTimeout(() => {
      const messageDetails = [
        { id: 1, title: "Message001", content: "message content00111...." },
        { id: 3, title: "Message003", content: "message content00222...." },
        { id: 5, title: "Message005", content: "message content00333...." },
      ];
      this.messageDetails = messageDetails;
      const id = this.$route.params.id;
      this.detail = this.messageDetails.find((detail) => detail.id === id * 1);
    });
  },
};
</script>

<style>
</style>

修改我们的src/router/index.js

一个“路径参数”使用冒号(:)标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。

/*
路由器对象模块
 */
import Vue from 'vue'
import VueRouter from 'vue-router'

import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'
import MessageDetail from '../pages/MessageDetail.vue'

// 声明使用vue-router插件
Vue.use(VueRouter)


// 定义routes路由的集合,数组类型
const routes = [{
    path: '/about',
    component: About
  },
  {
    path: '/home',
    component: Home,
    children: [{
        path: '/home/news',
        component: News
      },
      {
        path: 'message',
        component: Message,
        children: [{
          path: 'detail/:id',
          component: MessageDetail
        }]
      },
      {
        path: '',
        redirect: '/home/news'
      }
    ]
  },
  {
    path: '',
    redirect: '/home'
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

配置好了,不出意外是能正常运行的,我们来看一下效果:

在这里插入图片描述

你可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $route.params 中。例如:

模式匹配路径$route.params
/user/:username/user/evan{ username: ‘evan’ }
/user/:username/post/:post_id/user/evan/post/123{ username: ‘evan’, post_id: ‘123’ }

除了 $route.params 外,$route 对象还提供了其它有用的信息,例如,$route.query (如果 URL 中有查询参数)、$route.hash 等等。你可以查看 API 文档 的详细说明。

<div id="root">
  <router-link to="/login?id=10&name='tom'">登录1</router-link>
  <router-link to="/login/20/jack">登录2</router-link>
  <router-link to="/register">注册</router-link>
  <router-view></router-view>
</div>
<script type="text/javascript">
  const login = {
    template:'<h1>登陆组件</h1>',
    created(){
      /*
       $route
       一个路由对象表示当前激活的路由的状态信息,
       包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录
      */
      console.log('路由对象==>', this.$route);
      /*
       $route.query
       一个 key/value 对象,表示 URL 查询参数。
       例如,对于路径 /login?id=10&name='tom',
       则有 $route.query.id == 10 和 $route.query.name == 'tom'
       如果没有查询参数,则是个空对象。
      */
      console.log('路由对象query属性==>', this.$route.query);
      console.log('路由对象query属性==>', this.$route.query.id);
      console.log('路由对象query属性==>', this.$route.query.name);
      /*
       $route.params
       一个 key/value 对象,包含了动态片段和全匹配片段,
       例如,对于路径 /login/:id/:name --> /login/20/jack,
       则有 $route.params.id == 20 和 $route.params.name == 'jack'
       如果没有路由参数,就是一个空对象。
      */
      console.log('路由对象params属性==>', this.$route.params);
      console.log('路由对象params属性==>', this.$route.params.id);
      console.log('路由对象params属性==>', this.$route.params.name);
    }
  };
  const register = {template:'<h1>注册组件</h1>'};
  var router = new VueRouter({
    routes:[
      {path:'/', redirect:'/login'},
      {path:'/login', component:login},
      {path:'/login/:id/:name', component:login},
      {path:'/register', component:register}
    ],
    //设置激活样式
    linkActiveClass:'router-active'
  });
  var vue = new Vue({
    el: '#root',
    router
  });
</script>
</body>

响应路由参数的变化

提醒一下,当使用路由参数时,例如从 /home/message/detail/1 导航到 /home/message/detail/3原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:

修改MessageDetail.vue的代码

<template>
  <ul>
    <li>id: {{ $route.params.id }}</li>
    <li>title: {{ detail.title }}</li>
    <li>content: {{ detail.content }}</li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      messageDetails: [],
      detail: {},
    };
  },
  mounted() {
    // 改变当前路由组件参数数据时, 不会重新创建组件对象, mounted不会重新执行
    setTimeout(() => {
      const messageDetails = [
        { id: 1, title: "Message001", content: "message content00111...." },
        { id: 3, title: "Message003", content: "message content00222...." },
        { id: 5, title: "Message005", content: "message content00333...." },
      ];
      this.messageDetails = messageDetails;
      const id = this.$route.params.id;
      this.detail = this.messageDetails.find((detail) => detail.id === id * 1);
    });
  },

  watch: {
    $route: function () {
      // 改变当前路由组件参数数据时自动调用
      console.log("$route()");
      const id = this.$route.params.id;
      this.detail = this.messageDetails.find((detail) => detail.id === id * 1);
    },
  },
};
</script>

<style>
</style>

我们可以很明显的看到我们监听的$route对象被触发了,控制台也输出了;

或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫

const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
}

捕获所有路由或 404 Not found 路由

常规参数只会匹配被 / 分隔的 URL 片段中的字符。如果想匹配任意路径,我们可以使用通配符 (*):

{
  // 会匹配所有路径
  path: '*'
}
{
  // 会匹配以 `/user-` 开头的任意路径
  path: '/user-*'
}

当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器

当使用一个通配符时,$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 使用 path-to-regexp 作为路径匹配引擎,所以支持很多高级的匹配模式,例如:可选的动态路径参数、匹配零个或多个、一个或多个,甚至是自定义正则匹配。查看它的 文档 学习高阶的路径匹配,还有 这个例子 展示 vue-router 怎么使用这类匹配。

匹配优先级

有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。

七、缓存路由组件对象

理解

  1. 默认情况下, 被切换的路由组件对象会死亡释放, 再次回来时是重新创建的

  2. 如果可以缓存路由组件对象, 可以提高用户体验

编码

给About.vue添加一个输入框,给里面填写数据;当我们切换到Home,在切换回来的时候,数据没有了;如果我们要还留着这些数据,那么就需要缓存组件对象;

注:对于高实时性页面不适用;

<template>
  <div>
    <h2>关于我们页面</h2>
    <input type="text" v-model="userName" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      userName: "",
    };
  },
};
</script>

<style>
</style>

实现方式

只需要给要显示该组件的<router-view></router-view>包裹一层<keep-alive>即可;

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

八、编程式的导航

Vue路由导航两种方式:

**标签导航:**标签导航<router-link><router-link>是通过转义为<a></a>标签进行跳转,其中router-link标签中的to属性会被转义为a标签中的href属性;

<router-link to="/home/news">新闻中心</router-link>

**编程式导航:**我们可以通过this.$router.push()这个方法来实现编程式导航,当然也可以实现参数传递,这种编程式导航一般是用于按钮点击之后跳转

相关API

push方法

this.$router.push(path):	相当于点击路由链接(可以返回到当前路由界面)

注意:在 Vue 实例内部,你可以通过 $router 访问路由器实例。因此你可以调用 this.$router.push()

想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

声明式编程式
<router-link :to="...">router.push(...)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

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

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

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

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

注意:如果提供了 path,params 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path:

const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

同样的规则也适用于 router-link 组件的 to 属性。

replace方法

this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)

router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式编程式
<router-link :to="..." replace>router.replace(...)

前进后退方法

this.$router.back(): 请求(返回)上一个记录路由

this.$router.go(-1): 请求(返回)上一个记录路由

this.$router.go(1): 请求下一个记录路由

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

例子

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)
router.back()

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

操作 History

你也许注意到 router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go 好像, 实际上它们确实是效仿 window.history API 的。因此,如果你已经熟悉 Browser History APIs,那么在 Vue Router 中操作 history 就是超级简单的。

应用案例

在消息列表页添加按钮,查看消息详情;

<template>
  <div>
    <ul>
      <li v-for="m in messages" :key="m.id">
        <router-link :to="`/home/message/detail/${m.id}`">{{m.title}}</router-link>
        <button @click="pushShow(m.id)">push查看</button>
        <button @click="replaceShow(m.id)">replace查看</button>
      </li>
    </ul>
    <button @click="$router.back()">回退</button>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        messages: [
         /* {id: 1, title: 'Message001'},
          {id: 3, title: 'Message003'},
          {id: 5, title: 'Message005'}*/
        ]
      }
    },

    mounted () {
      setTimeout(() => {
        const messages = [
          {id: 1, title: 'Message001'},
           {id: 3, title: 'Message003'},
           {id: 5, title: 'Message005'}
        ]
        this.messages = messages
      }, 1000)
    },$ro

    methods: {
      pushShow (id) {
        this.$router.push(`/home/message/detail/${id}`)
      },

      replaceShow(id) {
        this.$router.replace(`/home/message/detail/${id}`)
      }
    }
  }
</script>

<style>

</style>

九、命名路由

概述

有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

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

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

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

这跟代码调用 router.push() 是一回事:

router.push({ name: 'user', params: { userId: 123 }})

这两种方式都会把路由导航到 /user/123 路径。

应用案例

在路由模块中对路由进行命名

/*
路由器对象模块
 */
import Vue from 'vue'
import VueRouter from 'vue-router'

import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import News from '../pages/News.vue'
import Message from '../pages/Message.vue'
import MessageDetail from '../pages/MessageDetail.vue'

// 声明使用vue-router插件
Vue.use(VueRouter)


// 定义routes路由的集合,数组类型
const routes = [{
    path: '/about',
    name: "about",
    component: About
  },
  {
    path: '/home',
    name: "home",
    component: Home,
    children: [{
        path: '/home/news',
        name: "homeNews",
        component: News
      },
      {
        path: 'message',
        name: "homeMessage",
        component: Message,
        children: [{
          path: 'detail/:id',
          name: "messageDetail",
          component: MessageDetail
        }]
      }
    ]
  },
  {
    path: '',
    redirect: '/home'
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

在不同的组件中使用命名路由

<!-- App-->
<router-link :to="{ name: 'home' }" class="list-group-item">Home</router-link>
<router-link :to="{ name: 'about' }" class="list-group-item">About</router-link>

<!-- Home-->
<ul class="nav nav-tabs">
    <li><router-link :to="{ name: 'homeNews' }">新闻中心</router-link></li>
    <li><router-link :to="{ name:'homeMessage' }">消息中心</router-link></li>
</ul>

在Message路由中使用命名路由并且在编程式路由中使用命名路由;

<template>
  <div>
    <ul>
      <li v-for="m in messages" :key="m.id">
        <!-- <router-link :to="`/home/message/detail/${m.id}`">{{m.title}}</router-link> -->
        <router-link :to="{name:'messageDetail',params:{id:m.id}}">{{m.title}}</router-link>
        <button @click="pushShow(m.id)">push查看</button>
        <button @click="replaceShow(m.id)">replace查看</button>
      </li>
    </ul>
    <button @click="$router.back()">回退</button>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        messages: [
         /* {id: 1, title: 'Message001'},
          {id: 3, title: 'Message003'},
          {id: 5, title: 'Message005'}*/
        ]
      }
    },

    mounted () {
      setTimeout(() => {
        const messages = [
          {id: 1, title: 'Message001'},
           {id: 3, title: 'Message003'},
           {id: 5, title: 'Message005'}
        ]
        this.messages = messages
      }, 1000)
    },

    methods: {
      pushShow (id) {
        this.$router.push({name:'messageDetail',params:{id}})
      },

      replaceShow(id) {
        this.$router.replace({name:'messageDetail',params:{id}})
      }
    }
  }
</script>

<style>

</style>

十、命名视图

概述

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 header (头部),sidebar (侧导航) 和 main (主内容) 三个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。

<router-view class="view one"></router-view>
<router-view class="view two" name="aside"></router-view>
<router-view class="view three" name="main"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        // 视图名:组件
        default: Header,
        aside: Aside,
        main: Main
      }
    }
  ]
})

应用案例

Header.vue

<template>
  <div class="header">
    <h1>header头部</h1>
  </div>
</template>

<script>
export default {};
</script>

<style>
.header {
  background-color: #1b6d85;
  height: 80px;
  margin: 0px;
}
</style>

Aside.vue

<template>
  <div class="left">
    <h1>left导航部分</h1>
  </div>
</template>

<script>
export default {};
</script>

<style>
.left {
  background-color: #3c763d;
  flex: 2;
}
</style>

Main.vue

<template>
  <div class="main">
    <h1>main主体部分</h1>
  </div>
</template>

<script>
export default {};
</script>

<style>
.main {
  background-color: #5bc0de;
  flex: 8;
}
</style>

App.vue

<template>
  <div>
    <router-view></router-view>
    <div class="container">
      <router-view name="aside"></router-view>
      <router-view name="main"></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
html,
body,
h1 {
  margin: 0;
  padding: 0;
}
.container {
  display: flex;
  height: 600px;
}
</style>

router/index.js

/*
路由器对象模块
 */
import Vue from 'vue'
import VueRouter from 'vue-router'

import Header from '../pages/Header.vue'
import Aside from '../pages/Aside.vue'
import Main from '../pages/Main.vue'

// 声明使用vue-router插件
Vue.use(VueRouter)


// 定义routes路由的集合,数组类型
const routes = [{
    path: '/header',
    name: "header",
    component: Header
  },
  {
    path: '/aside',
    name: "aside",
    component: Aside,
  },
  {
    path: '/main',
    name: "main",
    component: Main,
  },
  {
    path: '/',
    components: {
      'default': Header,
      'aside': Aside,
      'main': Main
    }
  }
]

// 实例化VueRouter并将routes添加进去
const router = new VueRouter({
  // ES6简写,等于routes:routes
  routes,
  //设置激活样式
  linkActiveClass: 'router-active'
})

// 抛出这个这个实例对象方便外部读取以及访问
export default router

十一、重定向和别名

重定向

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

甚至是一个方法,动态返回重定向目标:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a 路由添加一个 beforeEach 守卫并不会有任何效果。

别名

“重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b,那么“别名”又是什么呢?

/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

上面对应的路由配置为:

const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

“别名”的功能让你可以自由地将 UI 结构映射到任意的 URL,而不是受限于配置的嵌套路由结构。

十二、路由传参

query

  • 参数设置:通过 ? 拼接路由路径中。
  • 参数获取:$route.query 一个 key/value 对象,表示 URL 查询参数,如果没有查询参数,则是个空对象。
  • 对于路径 /register?username=tom&password=123,则有 $route.query.username == tom$route.query.password == 123

params

  • 参数设置:通过路由路径传参。
  • 参数获取:$route.params 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
  • 对于路径 /login/:username/:password --> /login/jack/456,则有 $route.params.username == jack$route.params.password == 456

props

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。

如果 props 被设置为 true,route.params 将会被设置为组件属性。

十三、导航守卫

“导航”表示路由正在发生改变。vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

全局前置守卫

你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。

每个守卫方法接收三个参数:

  • to: Route对象,即将要进入的目标路由对象
  • from: Route对象,当前导航正要离开的路由
  • next: Function函数,一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。

这里有一个在用户未能验证身份时重定向到 /login 的示例:

// BAD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated){
    next({ name: 'Login' })
  }
  // 如果用户未能验证身份,则 `next` 会被调用两次
  next()
})
// GOOD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated){
    next({ name: 'Login' })
  } else {
    next()
  }
})

全局解析守卫

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

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

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

这些守卫与全局前置守卫的方法参数是一样的。

组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

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

beforeRouteEnter 守卫不能访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 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)
  }
}

完整的导航解析流程

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

十四、路由元信息

定义路由的时候可以配置 meta 字段:

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

那么如何访问这个 meta 字段呢?

首先,我们称呼 routes 配置中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,他可能匹配多个路由记录

例如,根据上面的路由配置,/foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。

一个路由匹配到的所有路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。因此,我们需要遍历 $route.matched 来检查路由记录中的 meta 字段。

下面例子展示在全局导航守卫中检查元字段:

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()
  }
})

十五、路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

为给客户更好的客户体验,首屏组件加载速度更快一些,解决白屏问题。

懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载。

常用的懒加载方式有两种:即使用vue异步组件ES中的import

未用懒加载,vue中路由代码如下:

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
    routes: [
        {
            path: '/',
            name: 'HelloWorld',
            component:HelloWorld
        }
    ]
})

vue异步组件实现懒加载

方法如下:component:resolve=>(require([‘需要加载的路由的地址’]),resolve)

import Vue from 'vue'
import Router from 'vue-router'
  /* 此处省去之前导入的HelloWorld模块 */
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: resolve=>(require(["@/components/HelloWorld"],resolve))
    }
  ]
})

ES 提出的import方法(最常用)

方法如下:const HelloWorld = ()=>import(‘需要加载的模块地址’) (不加 { } ,表示直接return)

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const HelloWorld = ()=>import("@/components/HelloWorld")
export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component:HelloWorld
    }
  ]
})

路由懒加载和组件懒加载:https://www.cnblogs.com/xiaoxiaoxun/p/11001884.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JTZ001

你的鼓励是我创作的最大动力?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值