国庆七天假 不如来学学Vue-Router
阅读原文时间:2021年10月03日阅读:1

Vue-Router

Vue-Router是Vue全家桶中至关重要的一个扩展化插件,使用它能够让我们的组件切换更加的方便,更加容易的开发前后端分离项目,目前Vue-Router版本已更新到4.x,我们就以4.x为例了解它的用法。

官方文档

学习Vue-Router之前,我们有必要了解一下什么是后端路由。

在早期前后端混合开发时,前端具体所展示的内容是由后端负责的,也就是说用户在浏览器中的一次请求操作会直接传递到后端服务器上,后端服务器会根据这个请求内容返回给浏览器不同的页面,如下所示:

现在的项目越来越大,前后端混合的开发模式并不适用于大型项目,于是出现了前后端分离的开发模式,即用户在页面上所有静态资源请求大部分都是面向前端服务器发起的,然后再由前端服务器动态请求后端服务器的API接口获得所需要展示的数据最终结合前端服务器中的静态资源反馈给浏览器:

前置知识

用户与Web服务器之间是经由浏览器通过url打交道的,因此在学习Vue-Router插件之前你应该掌握一些必要的url相关知识。

下面是url的组成部分,截图自Node.js官网中对url的描述:

值得注意的是,auth部分是对身份权限的认证,目前在绝大多数应用中已经极少看到了,因此这里不再进行介绍。

我们以下面这个url进行说明:

https://localhost/book/1?page=2#caption_01

释义如下:

  • https:代指请求协议,最常见的有http与https协议
  • localhost:为host主机部分,其实完整写法应该是localhost:80,但是浏览器会自动为我们填上一个80的端口号,所以上面没有显式的写出来
  • /book/1:这一段是path即请求的资源路径(服务器上不一定真实存在),通常情况下/book这一段是固定的。而/1这个只是一个书籍编号而已,它不是固定的,因此对于1来说他可以作为查询的params,即查询参数,这在RestAPI设计风格中极为常见
  • ?page=2:这一段是请求的具体参数,即query部分,指的是我们请求book1这本书从第2页开始
  • #caption_01:一个锚点的跳转,用于定位具体的内容,当打开这个url后它会直接跳转到第二章节,注意该部分绝大多数情况下都不会被路由系统所处理

Vue单页面开发的原理实际上就是利用标签的锚点切换来完成的,举个例子。

访问下面的这个url就是主页:

http://loclhost/#/index

跳转时只需要修改锚点的定位即可,如从首页跳转到新闻页面:

http://loclhost/#/news

下面我们基于此原理利用原生的HTML+CSS来实现一个单页面应用:

代码示例:

<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        list-style: none;
    }

    .components {
        display: none;
    }

    :target {
        display: block;
    }

    body header {
        background-color: #565656;
        height: 48px;
    }

    body header ul {
        height: 100%;
        display: flex;
        flex-flow: row wrap;
        align-items: center;
        padding: 1rem;
    }

    body header ul li:first-of-type,
    body header ul li:nth-of-type(2) {
        margin-right: 2rem;
    }

    body header ul li:last-of-type {
        margin-left: auto;
    }

    body header ul li a {
        color: #fff;
        text-decoration: none;
    }

    body main {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 68px;
        font-size: 5rem;
    }
</style>

<body>
    <header>
        <ul>
            <li><a href="#/index">主页</a></li>
            <li><a href="#/news">新闻</a></li>
            <li><a href="#/backend">后台</a></li>
        </ul>
    </header>
    <main>
        <!-- 主页 -->
        <div id="/index" class="components">
            <span>HELLO INDEX</span>
        </div>
        <!-- 新闻 -->
        <div id="/news" class="components">
            <span>HELLO NEWS</span>
        </div>
        <!-- 后台 -->
        <div id="/backend" class="components">
            <span>HELLO BACKEND</span>
        </div>
    </main>
    <footer></footer>
</body>

Vue-Router中对于组件的切换有2种模式,分别是hash模式和history模式。

当使用hash模式时,若你访问/index页面则浏览器地址栏中的url是这个样子的:

http://loclhost/#/index

而使用history模式时,url看起来会正常许多:

http://loclhost/index

使用hash模式的优缺点如下:

  • 优点是#后面的信息永远不会被发送到Vue服务器的路由上,因此不会出现请求错误的情况
  • 缺点是对SEO会有不好的影响,以及看起来不是那么美观

而使用history模式的优缺点如下:

  • 优点是组件的切换看起来不再是锚点定位了,更加的美观了
  • 缺点是如果用户在浏览器中直接访问某个嵌套的子组件时,会报404错误,并且我们还需要在服务器上加上一个回退路由,所以配置比较麻烦

hash模式是我们在开发时建议使用的,尽管history模式让url看起来美观了许多,但是你需要做一些额外的配置,过程比较繁琐,你可以参阅官方文档寻找这部分的资料。

hash和history其实都是window的2个子对象,使用location.hash可修改当前url中#后的部分,比如我们在浏览器的控制台中输入以下命令:

location.hash = "/index"

你会发现浏览器的地址栏中url会变更为:

http://localhost:5500/#/index

同理,history模式也提供了一些链接跳转的方法支持。

如,在浏览器的控制台中输入以下命令:

history.pushState({}, "", "/index")

你会发现浏览器的地址栏中url会变更为:

http://localhost:5500/index

如果你想了解更多,请参照之前JavaScript中的widnow对象章节。

当浏览一个网页时,只要地址栏中的url发生改变都会被浏览器记录到一个“栈”中。

如下所示,我们先浏览了index.html、然后又浏览了news.html、最后浏览了backend.html:

当你点击back按钮或者调用router.back()以及history.back()方法后,它会向后回退一格,同时图中左侧的计数器也会发生变化:

同理,当你点击forward按钮或者调用router.forward()以及history.forward()方法后,它又会向前推进一格,回到图1的状态:

如果你通过router.go()或者history.go()方法,并且填入的参数是-2时,那么它会向后回退2格,变成下面这种状态:

而router.replace()的意思是,跳转至新的页面,并且清空整个栈,如下所示,它等同于history.replaceState()方法:

基本使用

使用Vite生成项目,跟随指引一步一步的进行初始化:

$ npm init @vitejs/app <project-name>

进入到项目根目录下,安装Vue-Router插件,输入以下命令后它将会安装Vue-Router 4.x的最新版本:

$ npm install vue-router@4

在src下新建一个router目录,并在其中新建一个index.js文件,该文件将作为Vue-Router插件的配置文件存在:

$ mkdir ./src/router
$ touch ./src/router/index.js

删除.src/components/HelloWorld.vue:

$ rm -rf ./src/components/HelloWorld.vue

清空.src/App.vue中的代码,并粘贴下面的代码:

<template>
  <div>hello world</div>
</template>

<script setup>
</script>

<style>

</style>

接下来你就可以启动这个Vue项目了,默认项目的端口是3000:

$ npm run dev

我们需要先准备3个组件、分别是Index、Book、Hot,代码基本一致:

<template>
  <div>
    <h1>Hello Index</h1>
  </div>
</template>

<script setup>
</script>

<style scoped>
h1 {
  font-size: 8rem;
}
</style>

要想使用Vue-Router插件,我们就必须在.src/router/index.js文件中对其进行引入:

// 1. 导入使用hash模式、使用history模式、以及创建全局路由对象的方法
import { createWebHashHistory, createWebHistory, createRouter } from "vue-router";

// 2. 导入组件
import Index from "../components/Index.vue"
import Book from "../components/Book.vue"
import Hot from "../components/Hot.vue"

// 3.配置路由映射表
const routes = [
    { path: "/index", component: Index },
    { path: "/book", component: Book },
    { path: "/hot", component: Hot },
]

// 4.创建全局路由对象,并且使用hash模式初始化这个全局路由对象
const router = createRouter({
    routes,
    history: createWebHashHistory()
})

// 5.导出全局路由对象
export default router

然后需要到.src/main.js中为当前的Vue应用加载这个插件:

import { createApp } from 'vue'
import App from './App.vue'
import router from "./router/index"

const app = createApp(App);
// 加载插件
app.use(router);
app.mount('#app');

最后修改一下App.vue的代码,我们要在App.vue中使用以及组件来做路由的入口和出口,注意以及组件都是Vue-Router所自带的组件,内部已完成全局注册,可以直接使用:

<template>
  <div>
    <header id="header">
      <!-- 路由入口,相当于a标签 -->
      <router-link :to="{ path: '/index' }">主页</router-link>
      <router-link :to="{ path: '/book' }">书籍</router-link>
      <router-link :to="{ path: '/hot' }">热门</router-link>
    </header>
    <main id="main">
      <!-- 路由出口,当点击<router-link>后,路由表中的组件会在此处显示 -->
      <router-view></router-view>
    </main>
  </div>
</template>

<script setup>
// 注册组件
import Index from "./components/Index.vue";
import Book from "./components/Book.vue";
import Hot from "./components/Hot.vue";
</script>

<style>
/* 全局样式设置 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  list-style: none;
}

header#header {
  height: 50px;
  padding: 1rem;
  background: #565656;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  box-shadow: #aaa 0 5px 5px;
}

header#header a {
  text-decoration: none;
  margin-right: 2rem;
  color: #fff;
}

main#main {
  display: flex;
  flex-flow: column;
  justify-content: center;
  align-items: center;
  height: 80vh;
}
</style>

实际上中的to属性也可以不用进行动态绑定,而是直接填入path,但是我们并不推荐这样使用:

<!-- 不推荐 -->
<router-link to="/index">主页</router-link>

<!-- 推荐 -->
<router-link :to="{ path: '/index' }">主页</router-link>

对.src/index.js里routes中每一条rule对象,我们都可以为它们加上一个name属性:

const routes = [
    { path: "/index", name: "index", component: Index },
    { path: "/book", name: "book", component: Book },
    { path: "/hot", name: "hot", component: Hot },
]

这样在为模板中的设置to属性时,可以动态的绑定一个对象并指定需要跳转的rule的name属性即可:

<header id="header">
  <!-- 路由入口,相当于a标签 -->
  <router-link :to="{ name: 'index' }">主页</router-link>
  <router-link :to="{ name: 'book' }">书籍</router-link>
  <router-link :to="{ name: 'hot' }">热门</router-link>
</header>

对.src/index.js里routes中每一条rule对象,我们都可以为它们加上一个alias属性:

const routes = [
    {
        path: "/index",
        name: "index",
        alias: ["/index.htm", "/index.html"],
        component: Index
    }
]

配置好之后不管你访问下面那个url,它都会跳转至Index组件中:

http://localhost:3000/index
http://localhost:3000/index.htm
http://localhost:3000/index.html

如果想在路由中定义重定向,则只需要配置redirect属性即可:

const routes = [
    {
        path: "/",
        redirect: { name: "index" }
    },
    {
        path: "/index",
        name: "index",
        alias: ["/index.htm", "/index.html"],
        component: Index
    },
]

这样当你访问 localhost:3000 时,它会自动跳转到Index中。

对.src/index.js里routes中每一条rule对象,我们都可以为它们加上一个meta属性:

const routes = [
    {
        path: "/index",
        name: "index",
        alias: ["/index.htm", "/index.html"],
        meta: { title: "主页", verify: false },
        component: Index
    }
]

在后续开发时,我们能通过一些方式拿到这些内容,因此你可以将meta属性定义成这个route的附加信息:

<script setup>
import { onMounted } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();

onMounted(() => {
  console.log(route.meta.verify);
  console.log(route.meta.title);
});
</script>

默认的点击后没有任何样式,我们可以在App.vue中配置一个全局的style,它的作用在于所有的点击后都会应用这个样式类,如下所示:

.router-link-active {
  text-shadow: #ddd 2px 5px 5px !important;
}

最终结果:

你也可以为每个单独添加active-class属性,然后定义属性值的class style,它的优先值高于.router-link-active:

// template
<router-link to="/index" active-class="choice_index">主页</router-link>

// style
.choice_index{
  color : red !important;
}

路由对象

useRouter()是Vue-Router中提供的一个构造方法,调用它可以得到一个全局的Router对象,即整个routes路由映射表。

如果想在模板中使用这个全局路由对象,你可以直接像下面这样操作:

{{$router}}

要想在脚本中使用这个全局路由对象你必须先对其进行导入:

import { useRouter } from "vue-router";

接下来需要进行实例化操作:

const router = useRouter();

全局路由对象中可以调用很多方法、如push()、back()等,如下表所示:

方法

描述

push()

等同于window.history.pushState(),打开一个新的url

replace()

等同于window.history.replaceState(),打开一个新的url,并清空当前活动窗口的历史记录

back()

等同于window.history.back(),执行页面回退操作

forward()

等同于window.history.forward(),执行页面前进操作

go(±n)

等同于window.history.go(),执行页面回退或前进n个操作

useRoute()是Vue-Router中提供的一个构造方法,调用它可以得到一个当前活动的Route对象,即当前所匹配到的rule。

如果想在模板中使用这个活动路由对象,你可以直接像下面这样操作:

{{$route}}

要想在脚本中使用这个活动路由对象你必须先对其进行导入:

import { useRoute } from "vue-router";

接下来需要进行实例化操作:

const route = useRoute();

活动路由对象中可以调用很多属性、如path、name等,如下表所示:

属性

描述

path

获取当前活动路由的path

name

获取当前活动路由的name

params

获取当前活动路由的params

query

获取当前活动路由的query

meta

获取当前活动路由的meta

hash

获取当前活动路由的hash

redirectedFrom

获取当前活动路由的前一个跳转链接

如果你想在