vue-router

一、 介绍

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

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

二、 安装

1. NPM

npm install vue-router

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

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

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

2. CDN

<!-- 最新版 -->
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<!-- 指定版本 在@后加上版本号 -->
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"></script>

三、 vue-router 搭建

将 vue-router 添加到项目后只需要配置路由的结构就可以将 vue 文件映射到对应的路由中了。

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

router.js

// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
  { path: "/foo", component: Foo },
  { path: "/bar", component: Bar }
];

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
  // (缩写) 相当于 routes: routes
  routes
});

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount("#app");

// 现在,应用已经启动了!

四、 动态路由

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:

const User = {
  template: "<div>User</div>"
};

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: "/user/:id", component: User }
  ]
});

带参跳转

this.$router.push({
  name: "songlist",
  params: {
    id
  }
});

五、 重定向和别名

1. 重定向

重定向也是通过 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 或 beforeLeave 守卫并不会有任何效果。

2. 别名

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

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

上面对应的路由配置为:

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

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

六、 导航守卫

“导航”表示路由正在发生改变。 监听路由的变化
记住参数或查询的改变并不会触发进入/离开的导航守卫

1. 全局前置守卫

你可以使用 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-linkto proprouter.push 中的选项。

next(error): (2.4.0+) 如果传入 next的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

!> 确保要调用 next 方法,否则钩子就不会被 resolved

2. 全局解析守卫

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

3. 全局后置钩子

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

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

实例

import store from "../store";
import router from "../router";

// let topPath = ['/music','/recommend', '/rank','/singer']
let topPath = ["/music"];

router.afterEach((to, from) => {
  // 判断当前是否一级路由, 是的话将启用侧边栏按钮
  let use = true;
  if (!topPath.includes(to.path)) {
    use = false;
  }
  // 当都是一级路由时不触发 useSideBar ,减少开销
  if (use != store.state.usesideBar) {
    store.commit("useSideBar", use);
  }
});
export default { router, store };

4. 路由独享的守卫

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

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

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

七、 路由过渡动效

<router-view> 是基本的动态组件,所以我们可以用 <transition> 组件给它添加一些过渡效果:

<transition>
  <router-view></router-view>
</transition>

1. 单个路由的过渡

上面的用法会给所有路由设置一样的过渡效果,如果你想让每个路由组件有各自的过渡效果,可以在各路由组件内使用 <transition> 并设置不同的 name

const Foo = {
  template: `
    <transition name="slide">
      <div class="foo">...</div>
    </transition>
  `
}

const Bar = {
  template: `
    <transition name="fade">
      <div class="bar">...</div>
    </transition>
  `
}

2. 基于路由的动态过渡

还可以基于当前路由与目标路由的变化关系,动态设置过渡效果:

<!-- 使用动态的 transition name -->
<transition :name="transitionName">
  <router-view></router-view>
</transition>
// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {
  '$route' (to, from) {
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
  }
}

八、 路由模式及原理

1. Hash模式

hash即浏览器url中#后面的内容,包含#

  • hash是URL中的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会加载相应位置的内容,不会重新加载页面。
  • 即#是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中,不包含#。
  • 每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。
  • 所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。
  • 每次 hash 值的变化,会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听 hashchange

2. History模式

  • 2014年后,因为HTML5标准发布。多了两个 API,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求, 让开发人员在不刷新整个页面的情况下修改站点的URL。

  • 当用户在浏览器点击进行后退、前进,或者在js中调用histroy.back(),history.go(),history.forward()等,会触发popstate事件;但pushState、replaceState不会触发这个事件。

3. 区别

1.直观区别:

  • hash模式url带#号,history模式不带#号。

2.深层区别:

  • hash模式url里面永远带着#号,我们在开发当中默认使用这个模式。

  • 如果用户考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url适合推广宣传

  • 功能也有区别,比如我们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react做的,

  • 咱们把这个页面分享到第三方的app里,有的app里面url是不允许带有#号的,所以要将#号去除那么就要使用history模式

  • 但是使用history模式还有一个问题就是,在访问二级页面的时候,做刷新操作,会出现404错误,那么就需要和后端人配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦。

路由模式配置:

 export default new Router({
     // mode: 'history',
     mode: 'hash',
     routes
 })

如果是 history模式需要后端配合解决刷新404问题 这里以Node 后台为例:

 const Koa = require('koa')
 const Router = require('koa-router');
 const static = require('koa-static')
 const fs = require('fs');
 const app = new Koa();
 const router = new Router();
 
 let str;
 fs.readFile('../dist/index.html', "utf-8", (err, data) => {
     if (err) {
         ctx.body = "error found"
     }
     str = data.toString();
 })
 
 // 解决vue 路由在 history刷新 404情况
 router.get('*', async(ctx, next) => {
     if (ctx.url !== "/index.html") {
         console.log("在这里返回")
         ctx.body = str;
     } 
 })
 
 app.use(static("../dist/"));
 app.use(router.routes()) //启动路由
 app.use(router.allowedMethods());
 
 
 app.listen(8989, () => {
     console.log("监听服务器地址:127.0.0.1:8989");
 })
Last Updated:
Contributors: zerojs