# Vue3 使用路由 Router

发布时间 2024-01-02 16:47:56作者: 我是ed

Vue3 使用路由 Router

之前几篇博文说了一下 vue 的基本语法和 vue 的传参,今天这篇博文稍微说一下 vue3 里面使用路由。

介绍

众所周知,vue 是用来构建单页面应用的前端框架,大于大多数此类型应用来讲,都推荐使用官方支持的 vue Router,在单页面应用,客户端的 JavaScript 可以连接页面跳转请求,动态获取数据,然后无需重新加载页面的情况下,更新当前页面数据,这样可以带来更加丝滑的用户体验,因为这类场景下的用户通常会在很长的一段时间中做出多次交互,路由是更新在客户端执行的。

vue Router 是 vue 官方路由,他与 vue 核心深度集成,让 vue 构建单页面应用变得更加轻而易举。

  • 嵌套路由映射
  • 动态路由选择 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

router 安装

安装 vue Router 只需要一个简单的命令即可实现安装:

npm install vue-router -S

执行完成之后,只需要静待安装完成即可。

安装完成之后,我们可以看到已经装了 4 版本的 router,如果是 vue2 的项目,则需要安装 3 版本的。

在这里插入图片描述

因为这两个版本他们是不互相兼容的,代码是不一样的,切记。

router 初始化

首先我们在 src 文件夹下创建一个 router 文件夹,在内部创建一个 index.ts 文件。

首先我们需要在这个 index.ts 文件中引入 router:

import { createRouter } from "vue-router";

然后我们初始化一下路由:

import { RouteRecordRaw, createWebHistory, createRouter } from "vue-router";

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    component: () => import('../components/HelloWorld.vue')  // 首页组件
  }, {
    path: '/about',
    component: () => import('../components/About.vue')  // 关于我们组件
  }
]

const router = createRouter({
  history: createWebHistory(), // 路由类型
  routes // short for `routes: routes`
})


export default router

然后,我们需要在 main.ts 文件中注册一下子:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')

这样的话,我们就把路由集成进项目了。

router-view

我们刷新一下页面,发现并没有任何效果,为啥子呢?

其实到这一步,我们已经将路由添加到项目里面去了,但是没有效果,是因为我们还没有写一个容器来引入我们的路由。

接下来我们写一个容器,在 App.vue 项目里面:

<script setup>
</script>

<template>
  <h1 class="ed-t">我是ed. vue3+router</h1>
  <router-view></router-view>
</template>

<style scoped>
.ed-t {
  padding: 10px;
  margin: 5px;
  color: hotpink;
}
</style>

像 vue2 项目一样,使用 <router-view></router-view> 插入路由。

这样的话我们刷新页面,可以看到我们能够根据路由变化切换组件更新显示内容:

在这里插入图片描述

注意: <router-view></router-view> 可以卸载任何位置,这个根据实际业务的排版来就可以。

接下来说一下 router-link ,这个是和 vue2 完全一样的,我们在 App.vue 文件编写 router-link

<template>
  <h1 class="ed-t">我是ed. vue3+router</h1>
  <div>
    <router-link class="ed-rl" to="/">首页</router-link>
    <router-link class="ed-rl" to="/about">关于我们</router-link>
  </div>
  <router-view></router-view>
</template>

这样的话,我们点击 router-link 的时候,可以快速实现组件切换,注意 router-link 必须有一个 to 属性,to 属性的值必须与初始化的 router 里面的 path 对应,意味着去哪个页面。

在这里插入图片描述

我们点击之后看到,下面的组件切换了,同时地址栏的地址也修改掉了。这就是路由最简单的使用方式。

路由模式

接下来说一下路由模式:

vue2 vue3
history createWebHistory
hash createWebHashHistory
abstact createMemoryHistory

上面是 vue2 和 vue3 路由类型的对比,其中 vue2 配置类型使用的属性是 mode, vue3 里面更新为 history

createWebHashHistory

上面的案例我们使用了 createWebHistory 模式:

在这里插入图片描述

看到访问的路由就是正常类似于多页面的地址。

但是如果使用了 createWebHashHistory 模式之后:

const router = createRouter({
  history: createWebHashHistory(),
  routes // short for `routes: routes`
})

我们看一下:

在这里插入图片描述

地址中间使用了 # 连接。

他是通过 location.hash 去匹配路由的:

在这里插入图片描述

比如我们让他跳转到首页:

在这里插入图片描述

就是这个样子。监听浏览器左右箭头,是使用一个回调函数实现的:

在这里插入图片描述

我们切换浏览器左右箭头就会触发打印:

在这里插入图片描述

createWebHistory

使用 createWebHistory 在地址栏是没有 # 号的。

它是基于 H5history 实现的:

在这里插入图片描述

它监听浏览器左右箭头是通过 popstate 实现的:

在这里插入图片描述

这时候,我们切换浏览器前后箭头,就可以打印出数据:

在这里插入图片描述

好的,就是这个样子。跳转的话是使用 pushState 实现跳转的:

在这里插入图片描述

使用这个切换了之后,你会发现页面地址栏地址已经变了,但是页面并没有修改,这是因为,你这种方式切换并不会监听到,还是需要手动刷新页面。

编程式导航

path 跳转

<template>
  <h1 class="ed-t">我是ed. vue3+router</h1>
  <div>
    <router-link class="ed-rl" to="/">首页</router-link>
    <router-link class="ed-rl" to="/about">关于我们</router-link>
  </div>
  <router-view></router-view>
</template>

上面的案例,我们是使用 router-link 标签通过 path 方式实现的路由跳转,除了使用 path 实现路由跳转之外,我们还可以使用 name 的方式进行路由的跳转。

name 跳转

比如我们给 routes 列表的路由配置添加一个名字:

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: "index",
    component: () => import('../components/HelloWorld.vue')
  }, {
    path: '/about',
    name: "about",
    component: () => import('../components/About.vue')
  }
]

注意哈,这个 name 不要起重复了嗷!

然后我们修改一下 router-link 标签,由 path 跳转改为 name 跳转:

<div>
  <router-link class="ed-rl" to="/">首页</router-link>
  <router-link class="ed-rl" :to="{name: 'about'}">关于我们</router-link>
</div>

我把 关于我们 改成通过 name 跳转了,可以对比一下子,效果一样一样滴!

在这里插入图片描述

a 标签跳转

我们还可以使用另一种方式,就是直接是 a 标签:

<a href="/">首页</a>
<a href="/about">关于我们</a>

但是这个和 path 跳转还是有区别的:

在这里插入图片描述

这个动图可能看不太清楚,我说一下吧,注意浏览器刷新按钮,我们切换页面的时候,按钮编程叉号一段时间,所以可以说明,使用这个方式实现页面跳转的话,会看到页面整体闪烁了一下子,他是整个页面给你刷新,而不是其中一部分刷新。因此不建议使用这种方式,只是知道就可以了。

编程式跳转

编程式跳转就是不通过便签实现路由的跳转,而是使用 js 代码的方式实现,用于我们点击按钮,手动进行跳转,或者是点击按钮,进行一些逻辑处理后在进行跳转。

那么我们可以在之前写 a 标签的地方改成两个按钮吧:

    <button class="ed-rl">首页</button>
    <button class="ed-rl">关于我们</button>

然后呢,我们给按钮添加个点击事件:

    <button @click="toPage('/')" class="ed-rl">首页</button>
    <button @click="toPage('/about')" class="ed-rl">关于我们</button>

然后我们写一下这个 toPage 事件:

<script setup lang="ts">
// 引入 hook
import { useRouter } from 'vue-router'
// 初始化一下
const router = useRouter()
// 页面跳转方法
const toPage = (url: string) => {
  // 跳转页面
  router.push(url)
}
</script>

这样的话,我们就是先了一个简单的编程式路由跳转:

在这里插入图片描述

效果是一样的。

router.push(url) 不仅仅可以传路由,他还可以传递一个对象,比如说用来跳转传参:

router.push({path: url})   // path 跳转

也可以是用 name 进行跳转:

router.push({name: 'about'})   // name 跳转

效果一样就不截图了。

历史纪录

上面的案例哈,我们点击按钮跳转完之后,我们可以通过浏览器的前进、后退按钮实现对应的操作。

在这里插入图片描述

因为通过 vue 路由的操作,会把历史纪录给存储起来。

但是,有时候嘞,我操作完,也就是页面跳转完成之后,我不想把历史纪录给存储起来,就比如说,我登录完成之后,我不想点击浏览器后退按钮在进入登录页面,这是后怎么办呢?

首先我们看 router-link 标签:

<router-link replace class="ed-rl" to="/">首页</router-link>

如果是 router-link 标签的话,我们可以直接使用一个 replace 设置这个路由不被保存到历史记录。

编程式开发

如果是使用编程式开发的话也很简单,就是把 push 改为 replace 即可:

router.replace(url)   // path 跳转

效果是一样的,也是没有历史纪录,效果一样就不截图了。

历史纪录逻辑操作

关于历史纪录的逻辑处理也很简单。

    <router-link class="ed-rl" to="/">首页</router-link>
    <router-link class="ed-rl" :to="{ name: 'about' }">关于我们</router-link>

    <button @click="prev()" class="ed-rl">向前</button>
    <button @click="next()" class="ed-rl">向后</button>

我们不用浏览器,点击自己的自定义按钮实现向前、向后切换功能:

const prev = () => {
  // router.go(-1)  // 参数是后退几个历史,比如1个,2个。
  router.back()  // 后退
}

const next = () => {
  router.go(1)  // 参数是前进几个历史,比如1个,2个。
}

看一下效果:

在这里插入图片描述

都是没有问题的!

路由传参

路由传参是项目里面肯定会用的功能,所以说这个得好好整一下,下面这一节,主要说一下关于路由传参的部分。

案例准备

先准备一个案例,我随便写的,咱就不要细说了关于这个案例,首先准备一个电影列表的 json 文件:

{
  "data": [
    {
      "name": "流浪地皮",
      "price": 29.9,
      "msg": "五星"
    },
    {
      "name": "我就是潘金莲",
      "price": 19.9,
      "msg": "四星"
    },
    {
      "name": "水壶传",
      "price": 9.9,
      "msg": "三星"
    }
  ]
}

然后编写一个组件展示一下:

<template>
  <p class="ed-title">电影列表</p>
  <p class="ed-item" @click="toPage(item, index)" v-for="item, index in data" :key="index">No.{{ index + 1 }} - 《{{ item.name }}》</p>
</template>

<script setup lang="ts">
import { data } from '../json/data.json'

type Film = {
  name: string,
  msg: string,
  price: number
}
// 点击电影项事件
const toPage = (item: Film , index: number) => {
  // todo: 跳转到新的页面,展示详细数据
}
</script>

样式我就不粘贴了,我们现看一下效果:

在这里插入图片描述

我们点击电影名称,跳转到 about 页面,展示详细信息,这时候,我们在点击事件里面需要实现两个功能,第一个是跳转,第二个是传参:

首先我们需要引入 router :

import { useRouter} from 'vue-router'

因为引入进来的是 hook,我们需要调用一下:

const router = useRouter()

好的,我们实现页面跳转:

const toPage = (item: film, index: number) => {
  router.push({
    path: '/about',
  })
}

好的,这样就实现了页面的跳转:

在这里插入图片描述

query 传参

然后,是传递参数,和 vue2 其实是一样一样的:

const toPage = (item: Film, index: number) => {
  router.push({
    path: '/about',
    query: item,
  })
}

注意,query 只能设置对象。上面我们是使用的 query 进行参数传递,

在这里插入图片描述

我们看到我们再次点击的时候,就会在地址栏显示我们传递的参数。

然后我们就可以在详情页面去取一下数据:

<template>
  <p class="ed-title">电影详情</p>
  <button @click="router.back()">返回</button>
  <p class="ed-t">名称:{{ route.query.name }}</p>
  <p class="ed-t">价格:¥{{ route.query.price }}</p>
  <p class="ed-t">备注:{{ route.query.msg }}</p>
</template>
<script setup lang="ts">
import { useRoute} from 'vue-router';
import { useRouter } from 'vue-router';
const route = useRoute()
const router = useRouter()

</script>
<style scoped>
.ed-title {
  font-size: 20px;
  font-weight: 550;
  color: rgb(6, 221, 236);
  padding: 10px;
  margin: 5px;
}

.ed-t {
  font-size: 16px;
  padding: 10px;
  margin: 5px;
}

button {
  margin: 15px;
}
</style>

然后,我们看一下效果:

在这里插入图片描述

效果实现了,可以传参并且展示出数据。

params 传参

注意 params 不能使用 path 进行参数传递,只能使用 name 进行传参。

所以修改上面页面跳转的代码:

const toPage = (item: Film, index: number) => {
  // router.push({
  //   path: '/about',
  //   query: item,
  // })

  router.push({
    name: 'about',
    params: item,
  })
}

上面代码就已经修改成 params 的方式进行参数传递了。params传参有一个特点,就是他传递的参数不会显示在地址栏:

在这里插入图片描述

看,点击之后,通过 params 传递参数的时候,地址栏已经不会显示传递的参数是什么了。

然后我们需要修改一下接受参数的地方,同样也是改为 params 接收参数:

  <p class="ed-t">名称:{{ route.params.name }}</p>
  <p class="ed-t">价格:¥{{ route.params.price }}</p>
  <p class="ed-t">备注:{{ route.params.msg }}</p>

这样就可以实现数据显示了。

但是,注意一个问题:

在这里插入图片描述
啥意思哈,就是从 4.1.4 版本之后,修改了route.params.name 之后也显示不出来,会出问题:

在这里插入图片描述

因为新版本把这个功能给砍掉了,怎么办呢?看下面官方提供的平替解决方案:

在这里插入图片描述

嵌套路由

官网

直接一个案例,和 vue2 完全一样,就不重复了:

import { RouteRecordRaw, createWebHashHistory, createWebHistory, createRouter } from "vue-router";

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: "footer",
    component: () => import('../components/Footer.vue'),
    children: [
      {
        path: '',
        name: "index",
        component: () => import('../components/HelloWorld.vue')
      }, {
        path: '/about',
        name: "about",
        component: () => import('../components/About.vue')
      }
    ]
  },

]

const router = createRouter({
  history: createWebHistory(),
  routes // short for `routes: routes`
})

export default router

命名视图

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航)main (主内容) 两个视图,这个时候命名视图就派上用场了。

你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

<router-view name="LeftSidebar"></router-view>
<router-view></router-view>
<router-view name="RightSidebar"></router-vie

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

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],
})

不常用,了解即可。

路由重定向 redirect

重定向比较简单一笔带过:

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: "footer",
    redirect: '/about',
    children: [
      {
        path: '/index',
        name: "index",
        component: () => import('../components/HelloWorld.vue')
      }, {
        path: '/about',
        name: "about",
        component: () => import('../components/About.vue')
      }
    ]
  },
]

主要是使用的 redirect: '/about', 这一段代码实现重定向功能。

除了直接设置,还可以使用对象的方式实现重定向:

redirect: {
  path: '/about'
},

可以设置 path ,当然设置 name 也是一样的:

redirect: {
  name: 'about'
},

除了上面两种方式,还可以设置一个回调:

    redirect: to => {
      console.log(to)
      return '/about'
    },

回调的话,我们可以接受一个参数 to,我们打印了 to 的信息,同时他需要返回一个路径:

在这里插入图片描述

打印出了他父路由的信息,ok,没问题!除了返回一个路径之外,同样也是可以返回一个对象实现传参:

    redirect: to => {
      console.log(to)
      return {
        path: '/about',
        query: {
          name: "我是ed."
        }
      }
    },

也是没有任何问题的,效果都一样。

在这里插入图片描述

OKOK,没有问题,参数也传递过去了!

路由别名 alias

alias 就是给我们的路由起多个名字,别名可以随便起,甚至可以取多个。

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: "footer",
    alias: ['/footer', '/footer1', '/footer2'],
    component: () => import('../components/Footer.vue')
  },
]

我们给这个路由设置了多个别名,我们访问哪一个别名之后呢,都可以访问到这个路由:

在这里插入图片描述

上面我们测试了一下别名,没有任何问题。

导航守卫

官网

router.beforeEach((to, from, next) => {
  // to 要前往的页面;from 从哪个页面来;next() 设置到哪个页面
  if(localStorage.getItem('token')) {
  	next(to.path)
  }else {
  	next("/login")
  }
})