Vue Router

介绍

Vue是一个典型的SPA单页面应用框架。SPA单页面是指网站只有一个html页面,所有的页面切换都只在这个html页面中完成。不同组件的切换全部交由路由来完成。

Vue Router 是 Vue.js 官方的客户端路由解决方案。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。

客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。

Vue Router 基于 Vue 的组件系统构建,通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。

理解路由器与路由的概念

在开始使用 Vue Router 之前,应该充分理解路由器与路由的概念。

在 Vue Router 中,“路由器(router)” 和 “路由(route)” 有特定的含义和作用。

路由器(router)

  • Vue Router 中的路由器是一个管理应用路由的对象。它负责协调和控制整个应用的导航过程。
  • 类似于网络中的物理路由器,它决定了用户在应用中的不同页面之间如何进行导航。
  • 路由器负责定义应用中的所有路由。通过配置路由对象数组,指定不同路径对应的组件。
  • 配置属性:
    • history:必需。定义路由的历史模式(可以理解为路由器的工作模式)。取值:

      • createWebHistory(): 使用浏览器的历史记录,适合部署在服务器上的应用。URL没有哈希符号(#)。
      • createMemoryHistory():在内存中管理历史记录,不依赖于浏览器的历史记录或 URL。适合在非浏览器环境中使用,如 Node.js等
      • createWebHashHistory():哈希模式。使用这种历史模式时,URL 中会包含一个哈希符号(#)。
        当页面中的路由发生变化时,浏览器不会向服务器发送请求来获取新的页面内容。Vue Router 通过监听浏览器地址栏中哈希部分的变化来触发路由的切换。
    • routes:必需。路由规则数组,定义了应用中的各个路由路径与对应的组件。

    • sensitive:可选。控制路由匹配是否区分大小写。

      • 取值类型:布尔值,默认值为 false(路由匹配不区分大小写)。
      • 当设置为true时,路由匹配区分大小写。例如,/About/about将被视为不同的路由路径。
    • strict:可选。控制带有结尾斜杠的路径和不带结尾斜杠的路径是否被视为不同的路由。

      • 取值类型:布尔值,默认值为 false(Vue Router 会自动添加或移除结尾斜杠以匹配路由)。
      • 当设置为true时,带有结尾斜杠的路径和不带结尾斜杠的路径将被视为不同的路由。例如,/about/about/将被视为不同的路由路径。

路由(route)

  • 代表应用中的一个特定的路由状态。它包含了当前路由的信息,如路径、参数、查询参数、组件等。
    • path:当前路由的路径。例如,如果当前页面的 URL 是 /about,那么 route.path 的值就是 /about
    • params:路由参数对象。当路由路径中包含参数时,可以通过这个对象获取参数的值。例如,对于路径 /user/:id,如果当前 URL 是 /user/123,那么 route.params.id 的值就是 123
    • query:查询参数对象。用于获取 URL 中的查询参数。比如 /search?q=vue,可以通过 route.query.q 获取查询参数 q 的值。
    • name:命名路由,具有唯一性。可以通过路由的名称进行导航。
    • matched:一个数组,包含当前路由匹配到的所有路由记录。可以用于获取当前路由的父路由信息等。
    • hash:URL 中的哈希值部分,如果有哈希路由或者锚点,这里会存储对应的哈希字符串。例如,对于URL /about#section1route.hash 的值是 '#section1'
    • fullPath:完成解析后的 URL,包含查询参数和哈希值。例如,对于 URL /search?q=vue&page=2#resultroute.fullPath 的值是 /search?q=vue&page=2#result

在 Vue Router 中,路由器是管理整个应用路由的核心对象,而路由则代表了特定的路由状态和信息。
一个路由(route)就是一组映射关系(key-value),多个路由需要路由器(router)进行管理。
在前端路由中,key是路径,value是组件,将路径与组件一一对应起来,这种对应关系就路由。

路由器与路由共同协作,实现了 Vue 应用的页面导航和路由管理功能。

安装 Vue Router

使用npm 直接安装:

npm install vue-router@4

如果启动一个新项目,可以使用 create-vue 脚手架工具创建一个基于 Vite 的项目,并包含加入 Vue Router 的选项

npm create vue@latest

基础使用

创建路由实例:

// router/index.ts

// 创建一个路由器,并暴露出去
// 第一步:引入createRouter, createWebHistory 
import { createRouter, createWebHistory } from 'vue-router';
// 引入要呈现的组件
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';

// 定义路由配置
// path 属性定义了路由的路径。 component 属性指定了与该路由对应的组件。
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
];

// 第二步:创建路由器
const router = createRouter({
  history: createWebHistory(), // 路由器的工作模式
  routes,
});

export default router;

main.ts 中,引入路由模块并将其传递给 Vue 应用实例。

// 引入 createApp 用于创建实例
import { createApp } from 'vue'
// 引入 App.vue 根组件
import App from './App.vue'

// 引入路由器
import router from './router'

// 创建一个应用
const app = createApp(App)

// 使用路由器
app.use(router)
// 挂载整个应用到app容器中
app.mount('#app')

app.use(router)实现以下功能:

  • 全局注册 RouterViewRouterLink 组件。
  • 添加全局 $router$route 属性。
  • 启用 useRouter()useRoute() 组合式函数。
  • 触发路由器解析初始路由。

在模板中使用 <router-link> 组件来创建链接进行页面导航。

<template>
  <div class="layout-wrap">
    <header class="layout-header-box">vue路由测试</header>
    <div class="layout-bottom-box">
      <aside class="layout-aside-box">
        <router-link to="/">首页</router-link>
        <router-link to="/about">关于我们</router-link>
      </aside>
      <section class="layout-section-box">
        <router-view></router-view>
      </section>
    </div>
  </div>
</template>

这段代码通过 Vue Router 的 <router-link><router-view> 组件实现了页面的路由导航和内容切换功能,为构建多页面应用提供了基础布局。

  • <aside class="layout-aside-box">:侧边栏区域,其中包含了2个 <router-link> 组件,分别用于导航到不同的路由页面。
    • <router-link to="/">首页</router-link>:点击这个链接会触发导航到路径为 / 的页面。
    • <router-link to="/about">关于</router-link>:点击导航到 /about 页面。
  • <section class="layout-section-box">:主要内容区域,用于显示通过路由切换进来的不同页面内容,由 <router-view> 组件占位,当导航到不同路由时,对应的组件内容会在这里显示。

当路由被匹配时,对应的组件会被渲染在 <router-view> 所在的位置。
当用户访问根路径时,Home 组件会被渲染;当访问 /about 路径时,About 组件会被渲染。

通过Vue Devtools查看组件:
在这里插入图片描述
查看路由:
在这里插入图片描述
现在活跃的路由是/Home 组件被渲染。

路由导航

在 Vue 中,路由导航主要通过 <router-link>编程式导航方法来实现。

<router-link>

<router-link> 是一个 Vue 组件,用于在模板中创建链接以进行页面导航。

使用组件 <router-link>创建链接,Vue Router 能够在不重新加载页面的情况下改变 URL,处理 URL 的生成、编码和其他功能。

例如:<router-link to="/about">关于我们</router-link>,当用户点击这个链接时,会导航到路径为 /about的页面。

<router-link>的属性

  • to:指定要导航到的目标路由路径。有两种写法:
    • 字符串写法:to="/about"
    • 对象写法::to="{ path: '/about' },可以是一个包含路径、查询参数、命名路由等信息的对象。
  • replace:控制路由跳转时操作浏览器历史记录的模式。
    • 默认值是false
    • 如果设置为 true,导航到一个新的 URL,同时替换当前的历史记录。
    • 示例:<router-link replace to="/news">News</router-link>
  • active-class:指定当链接处于激活状态时应用的 CSS 类名。默认是 "router-link-active"
  • exact:精确匹配模式,用于控制激活状态的判断方式。
    • 默认值为false
    • 当设置为true时,只有当链接的路径完全匹配当前路由路径时,才会应用激活状态的类名。
    • 例如: <router-link :to="/about" exact>关于页面</router-link>,如果当前路径是/about,则该链接会处于激活状态;如果当前路径是/about/contact,则该链接不会处于激活状态。
  • append:在相对路径的导航中,控制是否将目标路径附加到当前路径后面。
    • 默认值为false
    • 当设置为true时,相对路径的导航会将目标路径附加到当前路径后面。
<!-- 由于是绝对路径导航,无论当前在哪个页面,都会直接导航到/contact路径。 -->
<router-link to="/contact">绝对路径导航</router-link>
<!-- 不设置append或设置为false(默认值)  -->
<router-link to="../about" append>相对路径导航(不附加)</router-link>
<!-- 设置append为true-->
<router-link to="../about" append="true">相对路径导航(附加)</router-link>

假设当前页面的路径为 /parent/child

  • <router-link to="../about" append="false">相对路径导航(不附加)</router-link>,点击这个链接后导航的路径为 /parent/about
    相对路径 ../about 表示从当前路径向上一级(即从 child 回到 parent),然后再找 about 路径。不附加时,只考虑相对路径本身。
  • <router-link to="../about" append="true">相对路径导航(附加)</router-link>,点击这个链接后导航的路径为 /parent/child/about
    相对路径 ../about 表示从当前路径向上一级(即从 child 回到 parent),然后再找 about 路径。附加时,会将相对路径附加到当前路径后面,所以是 /parent/child/about

使用 <router-link> 创建 a 标签来定义导航链接

编程式导航

除了使用 <router-link> 创建 a 标签来定义导航链接,还可以使用 router 的实例方法,通过编写代码来实现。

在组件内部,使用 $router 属性访问路由。如果使用组合式 API,调用 useRouter() 来访问路由器。

  1. 选项式API可以使用 this.$routerthis.$route 访问路由器和当前路由。

    • this.$router用于访问全局的路由器实例。它允许你在组件中执行导航操作,如跳转到其他路由、获取路由信息等。
    • this.$route用于获取当前路由对象的信息。它提供了关于当前活动路由的各种信息,如路径、参数、查询参数等。
  2. 在组合式API中,因为在 setup 里面没有访问 this,所以不能直接访问 this.$routerthis.$route。使用 useRouteruseRoute 函数作为替代。

    • 注意:在模板中,仍然可以访问 $router$route。如果只在模板中使用这些对象的话,是不需要 useRouteruseRoute 的。
    • 示例:
<script lang="ts" setup>
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();
// 编程式导航,实现路由跳转到/about
const navigateToHome = () => {
  router.push('/about');
};
</script>

注意: 下面的示例中的 router 指代路由器实例。

router主要功能和方法

  • router.push()
    • 用于导航到不同的路由。会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
    • 接受一个字符串路径作为参数,或者一个包含路径、查询参数、路由参数等信息的对象。
    • 示例:
// 字符串路径
router.push('/news')

// 带有路径的对象
router.push({ path: '/news' })

// 带查询参数,结果是 /news?newsId=123
// 注意:如果提供了 path,params 会被忽略
router.push({ path: '/news', query: { newsId: 123 } })

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'news', params: { newsId: 123 } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })
  • router.replace():

    • 用于导航到一个新的 URL,同时替换当前的历史记录(不会向历史记录中添加新记录)。
  • router.go():

    • 用于在历史记录中前进或后退。接受一个整数作为参数(正数- 前进,0 - 刷新,负数 - 后退)。
    • 示例:this.$router.go(-1) 后退一步。
  • router.back():

    • 用于在历史记录中后退到上一个页面。
  • router.forward():

    • 用于在历史记录中前进到下一个页面。

当点击 <router-link> 时,内部会调用router.push方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)
由于属性 torouter.push 接受的对象种类相同,所以两者的规则完全相同。

路由传参

路由传参有多种方式: 通过查询字符串传递参数(query)、通过路由路径传递参数(params)、通过 路由的props配置 传参。

通过查询字符串传递参数(query

传递参数

  • 使用 router.push传递参数:
// 假设已经创建了 Vue Router 实例 router
router.push({ path: '/news', query: { newsId: 123 } });

// 也可以传递一个函数作为第二个参数,在函数中返回包含query参数的对象。
// 这个函数接收当前的路由对象作为参数,可以根据当前路由的状态来动态地设置query参数。
router.push({ path: '/news' }, (to) => {
  return { query: { newsId: 123,...to.query } };
});
  • 使用 <router-link>
    在 HTML 中的<router-link>组件上设置to属性时,可以直接在对象中包含query参数。
<!-- 跳转并携带query参数(to的字符串写法) -->
<router-link to="/news/detail?newsId=123">跳转</router-link>
				
<!-- 跳转并携带query参数(to的对象写法) -->
<router-link
  :to="{
    //name:'news', //用name也可以跳转
    path:'/news',
    query:{
      newsId: 123
    }
  }"
>
  跳转
</router-link>

在路由路径中看到 ? 时,意味着从这个位置开始后面的部分是查询参数。
?newsId=123 部分就是查询参数。

在组件中获取参数

  • 选项式API 通过 $route 直接访问query参数:
<template>
  <div>新闻ID: {{ $route.query.newsId }}</div>
</template>
<script>
export default {
  data() {
    return {};
  },
};
</script>
  • 组合式API 通过 useRoute 函数获取当前路由对象,然后从该对象中访问query参数:
<template>
  <div>新闻ID: {{ newsId  }}</div>
</template>

<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
const newsId = route.query.newsId ;
</script>

query注意事项

  1. query参数是一种松散类型的数据传递方式,可以传递字符串、数字、布尔值、对象、数组等各种类型的数据。
  2. query参数会在 URL 中显示,用户可以直接看到和修改(如果应用允许)。
  3. query参数在页面刷新后仍然保留,除非手动清除或者通过编程方式改变路由。

通过路由路径传递参数(params

定义路由时指定参数占位符

import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import News from "@/views/News.vue";

const routes = [
  { path: '/', component: Home },
  { path: '/news/:newsId', name: 'news', component: News},
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

这里的 :newsId 是路由参数占位符。当访问 /news/123 时,123 会被作为参数传递给 News.vue

传递参数

  • 使用 router.push 传递参数:
// 假设已经创建了 Vue Router 实例 router
// 传递参数进行导航
router.push({ name: 'news', params: { newsId: 123 } });

// 使用路径形式传递参数
router.push({ path: '/news/123' });
});
  • 使用 <router-link>
<!-- 跳转并携带params参数(to的字符串写法) -->
<router-link :to="/news/123">跳转</router-link>
				
<!-- 跳转并携带params参数(to的对象写法) -->
<router-link
  :to="{
    name:'news', //用name跳转
    params:{
      newsId: 123,
    }
  }"
>
  跳转
</router-link>

在组件中获取参数

  • 选项式API 通过 $route 直接访问params参数:
<template>
  <div>新闻ID: {{ $route.params.newsId }}</div>
</template>
<script>
export default {
  data() {
    return {};
  },
};
</script>
  • 组合式API 通过 useRoute 函数获取当前路由对象,然后从该对象中访问params参数:
<template>
  <div>新闻ID: {{ newsId  }}</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
// 获取路由参数
const newsId = computed(() => route.params.newsId);

// 使用newsId do something
</script>

在这个组件中,通过 useRoute 获取当前路由信息,然后使用计算属性 newsId 来获取动态路由参数 newsId 的值。
可以根据获取到的 newsId 参数进行数据获取和页面渲染。

params注意事项

  1. 传递params参数时,若使用to的对象写法,必须使用name配置项,不能用path
  2. 传递params参数时,需要提前在规则中占位。
  3. 当指定 params 时,可提供 stringnumber 参数。任何其他类型(如对象、布尔等)都将被自动字符串化。对于可选参数,你可以提供一个空字符串("")或 null 来移除它。

通过 路由的props配置 传参

在 Vue Router 中,通过设置props属性,可以将路由参数自动传递给组件的 props,使组件不直接依赖于$route对象,避免在组件内部通过$route.params来获取参数。

props: true

将路由收到的所有 params 参数作为 props 传给路由组件

const routes = [
  { path: '/news/:newsId', component: News, props: true, },
];

例如,当访问/news/123时,组件将接收一个名为newsId的 prop,其值为123

  • 在选项式API中,使用props属性接收参数:
<script lang="ts">
export default {
  props: ['newsId'],
}
</script>
  • 在组合式API中,使用defineProps()接收参数:
<script setup lang="ts">
defineProps(['newsId'])
</script>

newsId 具有响应性,跟随路由参数的改变而改变。
在这里插入图片描述

  • 在组合式API中,如果不defineProps()接收参数,则会将路由参数作为attrs传递给组件,通过useAttrs()访问attrs

当访问/news/123时,组件将接收一个名为newsId的 attr,其值为123

<script setup lang="ts">
import { toRefs, useAttrs } from 'vue';
const attrs = useAttrs();
const { newsId, name } = toRefs(attrs);

// 也可以使用computed属性
// import { computed, useAttrs } from 'vue';
// const attrs = useAttrs();
// 动态获取attrs,使attrs具有响应性
// const computedAttrs = computed(() => attrs);
// 动态获取newsId,使newsId具有响应性
// const newsId = computed(() => computedAttrs.value.newsId);
</script>

newsId 具有响应性,跟随路由参数的改变而改变。
在这里插入图片描述

props: function

可以设置为一个函数,该函数接收route对象作为参数,并返回一个对象。把返回的对象中每一组key-value作为props传给组件。

  • 可以自己决定将什么作为props给路由组件:
interface CustomRouteParams {
  id?: string;
}
const routes = [
  {
    path: '/news/:newsId',
    component: News,
    props: (route): { id: number; name: string } => {
        const { id } = route.params as CustomRouteParams;
        return {
          id: id ? parseInt(id) : 0,
          name: 'Default Name',
        };
      },
  },
];
  • 也可以直接把query参数作为 props 传给 路由组件:
const routes = [
  {
    path: '/news/:newsId',
    component: News,
    props: (route) => {
        return route.query;
      },
  },
];

参数接收方式同props: true

props的对象写法

也可以直接设置为一个对象,将静态的 props 传递给组件。这些 props 不会随着路由参数的变化而变化。

const routes = [
  {
    path: '/news/:newsId',
    component: News,
    props: {
      defaultName: 'Static Name',
    },
  },
];

参数接收方式同props: true

带参数的动态路由匹配

很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 News 组件,它应该对所有新闻进行渲染,但新闻 ID 不同。在 Vue Router 中,可以在路径中使用一个动态字段来实现,这个动态字段被称为 路径参数

路径参数 用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 route.params 的形式暴露出来。

const routes = [
  { path: '/news/:newsId', name: 'news', component: News},
];

/news/:newsId 是一个动态路由,其中 :newsId 是一个参数占位符。
现在,像 /news/123/news/765 这样的 URL 都会映射到同一个路由。

当访问/news/:newsId时,不同的 newsId 值会动态地匹配到这个路由,并且在组件中可以通过 $route.params.newsId(选项式 API)或 useRoute().params.newsId(组合式 API)来获取这个参数值。


可以在同一个路由中设置有多个 路径参数,它们会映射到 route.params 上的相应字段。

  • 匹配模式/news/:id
    • 匹配路径/news/123
    • route.params 值为:{ newsId: '123' }
  • 匹配模式/news/:newsId/isShare/:shareFlag
    • 匹配路径/news/123/isShare/true
    • route.params 值为:{ newsId: '123', shareFlag: 'true' }

响应路由参数的变化

在上面的例子中,当用户从/news/123 导航到 /news/765时,相同的组件实例将被重复使用
因为两个路由都渲染同个组件,组件被复用,组件的生命周期钩子不会被调用

由于生命周期钩子不被调用,不能依赖传统的钩子来进行数据获取。可以在 setup 函数中使用 watch 监听路由参数的变化(route.params的任意属性),当参数变化时触发数据获取操作:

<script setup lang="ts">
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

watch(() => route.params.newsId, (newId, oldId) => {
  // 对路由变化做出响应...
})
</script>

动态路由匹配 与 query 传参的区别

例如: /news/123/标题/news?newsId=123&title=标题

  1. URL 结构不同:动态路由匹配的参数是路由路径的一部分,而查询字符串的参数是在 URL 末尾通过 ?& 连接的键值对。
  2. 获取参数的方式不同:在组件中,动态路由的参数通过 params 获取,而查询字符串的参数通过 query 获取。
  3. 使用场景不同:
    • 动态路由匹配通常用于根据不同的参数值加载不同的资源或展示不同的内容,参数值与路由的逻辑关系更为紧密。
      • 例如,一个新闻应用中,/news/:newsId 可以根据不同的新闻 ID 加载不同的新闻详情页面。
    • 查询字符串则更适合传递一些额外的、不太影响路由逻辑的参数,或者用于临时的筛选、排序等操作。
      • 比如在新闻列表页面,可以通过查询字符串传递搜索关键词、排序方式等参数来筛选新闻列表。

路由的匹配语法

  1. 静态路由
    直接指定一个固定的路径,例如 /about表示当 URL 为根路径加上 /about时,匹配该路由。
{ path: '/about', component: About}
  1. 动态路径参数
    在路径中使用冒号 : 后跟参数名来定义动态路径参数。例如 /news/:newsId,当 URL 为 /news/123 时,其中 123 会被作为参数 newsId 的值。
{ path: '/news/:newsId', component: News}
  1. 使用?表示可选参数
    在参数后面加上? 表示该参数是可选的。例如 /news/:newsId?,URL 可以是 /news 或者 /news/123
    注意:用? 标记的参数不能重复。
{ path: '/news/:newsId?', component: News}
  1. 使用星号*表示通配符
    * 可以匹配任何路径。例如 /:catchAll(.*),这个路由会匹配所有未被其他路由匹配的路径。
    注意:用*标记的路由参数可以重复。
{ path: '/:catchAll(.*)', component: NotFound }

/:catchAll(.*) 这个路由会匹配所有未被其他路由匹配的路径,并渲染 NotFound 组件。

  1. 使用括号和正则表达式
    可以在参数的括号中使用正则表达式来更精确地控制参数的匹配规则。
{ path: '/user-:afterUser(.*)', component: UserGenericComponent }

这个路由规则匹配以 /user- 开头的路径。例如 /user-profile/user-settings 等。
:afterUser(.*) 是一个正则表达式捕获组,会将 /user- 后面的部分存储在 route.params.afterUser 中。

还可以写其它的正则:

// /:orderId -> 仅匹配数字
{ path: '/:orderId(\\d+)' }

// 正则表达式[a-zA-Z0-9]+ 限制参数 username 只能由字母和数字组成
{ path: '/user/:username([a-zA-Z0-9]+)' }

注意
在使用正则表达式时,确保转义反斜杠( \ ),就像 \d (变成\\d),在 JavaScript 中实际传递字符串中的反斜杠字符。

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

// 第一种写法:
{ path: '/:catchAll(.*)', component: NotFound }

// 第二种写法:
{ path: '/:pathMatch(.*)*', component: NotFound },

path: '/:catchAll(.*)'path: '/:pathMatch(.*)*'的区别:

  • path: '/:catchAll(.*)'
    这个表达式表示匹配所有路径,并将未匹配的部分存储在一个名为catchAll的参数中。
    它通常用于捕获所有未被其他特定路由匹配的路径,以显示 404 页面或进行通用的错误处理。

示例:

const routes = [ 
  { path: '/',  component: Home, },
  { path: '/news/:newsId', component: News, },
  { path: '/about', component: About, },
  { path: '/:catchAll(.*)', component: NotFound },
]

在浏览器访问路由中没有的/user/123
在这里插入图片描述

  • path: '/:pathMatch(.*)*'
    • :pathMatch(.*)* 使用了正则表达式捕获组,会将未匹配的路径部分存储在 route.params.pathMatch 中。
    • pathMatch用于捕获未匹配的路径部分。
    • 最后的*表示可以匹配零个或多个路径片段。

示例:

const routes = [ 
  { path: '/',  component: Home, },
  { path: '/news/:newsId', component: News, },
  { path: '/about', component: About, },
  { path: '/:pathMatch(.*)*', component: NotFound },
]

在浏览器访问路由中没有的/user/123
在这里插入图片描述

嵌套路由

嵌套路由允许在一个父路由下定义多个子路由,每个子路由对应不同的组件,从而实现页面的分层结构。当访问父路由时,可以通过在父路由组件中渲染<router-view>来显示子路由的内容。

在 Vue Router 的路由配置中,为父路由设置children属性来定义子路由:

import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import ParentComponent from './views/ParentComponent.vue';
import ChildComponent from './views/ChildComponent.vue';

const routes = [
  { path: '/', component: Home },
  {
    path: '/parent',
    component: ParentComponent,
    children: [
      {
        path: 'child',
        component: ChildComponent,
      },
    ],
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

在这个例子中,/parent是父路由,/parent/child是子路由。

在父路由对应的组件(ParentComponent)中,放置一个<router-view>标签来渲染子路由的内容。

<template>
  <div>
    <h2>Parent Component</h2>
    <router-view></router-view>
  </div>
</template>

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

可以使用<router-link>组件来导航到嵌套路由:

<router-link to="/parent/child">Go to Child Route</router-link>

路由守卫

在 Vue Router 中,路由守卫(Route Guards)用于控制路由的访问权限和导航流程。
路由守卫可以在路由切换的不同阶段执行特定的逻辑。
Vue Router 提供了以下几种类型的路由守卫: 全局路由守卫、路由独享守卫、组件内守卫。

全局路由守卫

全局前置守卫(beforeEach

  • 触发时机:在导航触发前执行,无论任何路由的切换都会经过这个守卫。
  • 使用方式:router.beforeEach((to, from, next) => {... })
  • 参数说明
    • to:即将要进入的目标路由对象。
    • from:当前导航正要离开的路由对象。
    • next:是一个必须调用的函数,用于决定导航的流程。不同的调用方式有不同的效果:
      • next():允许导航继续进行,即按照正常的路由切换流程进行下一步。如果在全局前置守卫(beforeEach)中调用,将继续执行后续的守卫和目标路由的加载。
      • next(false):中断当前的导航。如果在全局前置守卫或路由独享守卫中调用,会阻止导航到目标路由,并且不会触发后续的守卫和目标路由的加载。
      • next('/login'):将导航重定向到指定的路径。在全局前置守卫或路由独享守卫中调用时,会中断当前的导航,并将用户重定向到指定的路由。

示例:

import router from '@/router';
router.beforeEach((to, from, next) => {
  // 逻辑判断
  if (to.meta.requiresAuth &&!isAuthenticated()) {
    // 如果目标路由需要认证且用户未登录,重定向到登录页面
    next('/login');
  } else {
    next();
  }
});

全局后置守卫(afterEach

  • 触发时机:在导航完成后执行,无论导航成功还是被取消都会触发
  • 使用方式:router.afterEach((to, from) => {... })
  • 参数说明
    • to:进入的目标路由对象。
    • from:离开的路由对象。

示例:

router.afterEach((to, from) => {
  // 可以在这里记录页面访问日志等操作
  console.log(`Navigated from ${from.path} to ${to.path}`);
});

路由独享守卫

  • 定义位置:在单个路由的配置中定义。
  • 触发时机:仅在进入该特定路由时执行。

示例:

const routes = [
  {
    path: '/dashboard',
    component: DashboardComponent,
    meta: { requiresAuth: true },
    beforeEnter: (to, from, next) => {
      // 只有当访问 /dashboard 这个路由时才会执行这个守卫逻辑
      if (isAuthorizedForDashboard()) {
        next();
      } else {
        next('/access-denied');
      }
    },
  },
];

组件内守卫

在组件内部定义,可以更具体地控制组件在路由导航中的行为。

  1. beforeRouteEnter
  • 触发时机:在路由进入该组件前执行。
  • 使用方式:beforeRouteEnter(to, from, next) => {... }
  • 特点:
    • 此时组件实例还未创建,所以不能访问 this
    • 可以通过传递一个回调函数给 next 来访问组件实例。比如 next(vm => {... })
  1. beforeRouteUpdate
  • 触发时机:当路由参数变化但组件被复用时执行。
  • 使用方式:beforeRouteUpdate(to, from, next) => {... }
  1. beforeRouteLeave
  • 触发时机:在导航离开该组件前执行。
  • 使用方式:beforeRouteLeave(to, from, next) => {... }

在选项式API中使用:

<script>
import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const someData = ref(null);
    return { someData };
  },
  beforeRouteEnter(to, from, next) {
    // 可以在这里获取数据并在组件创建后使用
    fetchData(to.params.id).then(data => {
      next(vm => {
        // 访问组件实例,例如设置初始数据
        vm.someData = data;
      });
    });
  },
  beforeRouteUpdate(to, from, next) {
    // 处理路由参数变化等情况
    fetchData(to.params.id).then(data => {
      someData.value = data;
      next();
    });
  },
  beforeRouteLeave(to, from, next) {
    // 例如,提示用户是否保存未保存的更改
    if (inputValue.value) {
      const confirmLeave = window.confirm('You have unsaved changes. Are you sure you want to leave?');
      if (confirmLeave) {
        next();
      } else {
        next(false);
      }
    } else {
      next();
    }
  },
};
</script>

在组合式 API(<script setup>)中,Vue Router 将更新和离开守卫作为组合式 API 函数公开:

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'

// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm(
    'Do you really want to leave? you have unsaved changes!'
  )
  // 取消导航并停留在同一页面上
  if (!answer) return false
})

const userData = ref()

// 与 beforeRouteUpdate 相同,无法访问 `this`
onBeforeRouteUpdate(async (to, from) => {
  //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
  if (to.params.id !== from.params.id) {
    userData.value = await fetchUser(to.params.id)
  }
})
</script>

重定向

路由重定向是一种将用户从一个路由导航到另一个路由的方式。
重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /

const routes = [{ path: '/home', redirect: '/' }]

或者:

const routes = [{ path: '/home', redirect: { path: '/' } }]

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

const routes = [{ path: '/home', redirect: { path: 'homepage' } }]

使用函数进行重定向,动态返回重定向目标:

const routes = [
  {
    path: '/search/:searchText',
    redirect: to => {
      // 方法接收目标路由作为参数
      // return 重定向的字符串路径/路径对象
      return { path: '/search', query: { q: to.params.searchText } }
    },
  },
  {
    path: '/search',
    // ...
  },
]

函数接收一个参数to,代表目标路由对象,可以根据这个对象的属性或其他条件来决定重定向的目标路径。

导航守卫(如beforeEnter)只应用在目标路由上。如果进行了路由重定向,导航守卫只会在最终的目标路由上生效,而不会在跳转的中间路由上生效。
在上面的例子中,在 /home 路由中添加 beforeEnter 守卫不会有任何效果。

在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 childrenredirect 属性,它也应该有 component 属性。
因为在嵌套路由的情况下,父路由需要有一个组件来作为子路由的容器。即使父路由主要用于重定向,它仍然需要一个组件来承载子路由的渲染。

嵌套路由的重定向:

const routes = [
  {
    path: '/parent',
    redirect: '/parent/child',
    component: ParentComponent,
    children: [
      {
        path: 'child',
        component: ChildComponent,
      },
    ],
  },
];

这个例子中,/parent路由有重定向和子路由,所以需要有ParentComponent作为组件,以便在用户被重定向到/parent/child之前,能够有一个容器来渲染子路由的内容。

相对重定向

相对重定向根据当前路由的路径来动态地确定重定向的目标路径,而不是使用绝对路径进行重定向。

示例:

const routes = [
  {
    // 将总是把/users/123/posts重定向到/users/123/profile。
    path: '/users/:id/posts',
    redirect: to => {
      // 该函数接收目标路由作为参数
      // 相对位置不以`/`开头
      // 或 { path: 'profile'}
      return 'profile'
    },
  },
  {
     path: '/users/:id/profile',
     component: UserProfile,
   },
]

路由别名

路由别名(route alias)就是为一个路由定义一个或多个备用的路径名称,使得用户可以通过不同的 URL 访问相同的页面内容。

在 Vue Router 的路由配置中,使用alias属性来定义路由别名:

const routes = [
  { path: '/about', component: About, alias: '/about-us' },
];

/about路由定义了一个别名/about-us。用户可以通过/about/about-us访问About组件。

可以为一个路由定义多个别名,只需在alias属性中提供一个数组:

const routes = [
  { path: '/about', component: About, alias: ['/about-us', '/about-page'] },
];

用户可以通过/about/about-us/about-page访问About组件。

动态路由也可以定义别名,别名中的动态参数部分应该与原始路由中的动态参数部分保持一致:

const routes = [
  { path: '/about/:id', component: About, alias: '/about-us/:id' },
];

/about/:id/about-us/:id都将指向About组件,并且可以根据不同的 ID 进行动态路由匹配。

路由懒加载

在 Vue 应用中,路由懒加载是一种优化技术,它可以在需要的时候才加载特定的路由组件,而不是在应用初始化时一次性加载所有的路由组件。这样可以显著提高应用的初始加载速度,特别是当应用有很多路由和大型组件时。

在 Vue Router 中使用动态导入(import ())实现路由懒加载:

// 将
// import About from '@/views/About.vue'
// 替换成
const About = () => import('@/views/About.vue')
const routes = [
  {
    path: '/about',
    component: About ,
  },
  {
    path: '/some-route',
    component: () => import('./SomeComponent.vue'),
  },
];

在这个例子中,About.vue组件将在访问/about路由时才被动态加载,SomeComponent.vue组件将在访问/some-route路由时才被动态加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值