# Vue3 使用路由 Router

Vue3 使用路由 Router

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

介绍

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

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

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

router 安装

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

bash 复制代码
npm install vue-router -S

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

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

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

router 初始化

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

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

typescript 复制代码
import { createRouter } from "vue-router";

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

typescript 复制代码
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 文件中注册一下子:

bash 复制代码
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 项目里面:

html 复制代码
<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

html 复制代码
<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 模式之后:

typescript 复制代码
const router = createRouter({
  history: createWebHashHistory(),
  routes // short for `routes: routes`
})

我们看一下:

地址中间使用了 # 连接。

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

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

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

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

createWebHistory

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

它是基于 H5history 实现的:

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

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

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

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

编程式导航

path 跳转

html 复制代码
<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 列表的路由配置添加一个名字:

typescript 复制代码
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 跳转:

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

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

a 标签跳转

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

html 复制代码
<a href="/">首页</a>
<a href="/about">关于我们</a>

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

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

编程式跳转

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

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

html 复制代码
    <button class="ed-rl">首页</button>
    <button class="ed-rl">关于我们</button>

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

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

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

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

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

效果是一样的。

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

typescript 复制代码
router.push({path: url})   // path 跳转

也可以是用 name 进行跳转:

typescript 复制代码
router.push({name: 'about'})   // name 跳转

效果一样就不截图了。

历史纪录

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

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

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

首先我们看 router-link 标签:

html 复制代码
<router-link replace class="ed-rl" to="/">首页</router-link>

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

编程式开发

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

typescript 复制代码
router.replace(url)   // path 跳转

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

历史纪录逻辑操作

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

typescript 复制代码
    <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>

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

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

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

看一下效果:

都是没有问题的!

路由传参

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

案例准备

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

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

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

typescript 复制代码
<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 :

typescript 复制代码
import { useRouter} from 'vue-router'

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

typescript 复制代码
const router = useRouter()

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

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

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

query 传参

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

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

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

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

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

typescript 复制代码
<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 进行传参。

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

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

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

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

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

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

typescript 复制代码
  <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 完全一样,就不重复了:

typescript 复制代码
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

typescript 复制代码
<router-view name="LeftSidebar"></router-view>
<router-view></router-view>
<router-view name="RightSidebar"></router-vie

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

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

不常用,了解即可。

路由重定向 redirect

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

typescript 复制代码
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', 这一段代码实现重定向功能。

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

typescript 复制代码
redirect: {
  path: '/about'
},

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

typescript 复制代码
redirect: {
  name: 'about'
},

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

typescript 复制代码
    redirect: to => {
      console.log(to)
      return '/about'
    },

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

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

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

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

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

路由别名 alias

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

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

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

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

导航守卫

官网

typescript 复制代码
router.beforeEach((to, from, next) => {
  // to 要前往的页面;from 从哪个页面来;next() 设置到哪个页面
  if(localStorage.getItem('token')) {
  	next(to.path)
  }else {
  	next("/login")
  }
})
相关推荐
y先森5 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy5 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189115 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿6 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡7 小时前
commitlint校验git提交信息
前端
虾球xz8 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇8 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒8 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员8 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐8 小时前
前端图像处理(一)
前端