Learning Vue学习(chapter 8路由)
本文最后更新于55 天前,其中的信息可能已经过时。

chapter 8(路由)

RouteRecordRaw(Vue Router 路由配置对象)

1. 核心概念

在 Vue Router 中,路由(Route) 是通过 RouteRecordRaw 接口定义的配置对象,描述了一个 URL 路径(path)与对应组件(component)的映射关系。它是 Vue Router 的路由配置基础。


2. 核心属性

一个典型的 RouteRecordRaw 对象包含以下关键属性:

属性名类型说明
pathstring路径模式(如 '/about', '/user/:id'),匹配 URL 的路径部分。
componentComponent匹配路径时渲染的组件(单个组件或组件对象)。
namestring路由名称(可选),用于通过 router-linkrouter.push 精确跳转。
childrenRouteRecordRaw[]嵌套路由配置,将子路由挂载到当前路由的 <router-view> 中。
redirectstringFunction`
aliasstringArray`
metaany自定义元数据(如权限标记、页面标题),可在守卫中访问。
beforeEnterNavigationGuard路由独享守卫,仅对当前路由生效。

3. 基础示例

import { type RouteRecordRaw } from "vue-router"
const routes: RouteRecordRaw[] = [
  {
    path: '/about',
    component: () => import('@/views/About.vue'),
    name: 'AboutPage',
    meta: { requiresAuth: false },
  },
  {
    path: '/user/:id',
    component: UserView,
    children: [
      { path: 'profile', component: Profile },
      { path: 'settings', component: Settings },
    ],
  },
];

4. 高级配置

4.1 动态路由参数
{
  path: '/user/:id',
  component: UserView,
}
// URL 示例:/user/123 → params.id = '123'
4.2 路由重定向
{
  path: '/home',
  redirect: '/dashboard', // 跳转到 '/dashboard'
},
{
  path: '/old-path',
  redirect: to => {
    return '/new-path';
  },
}
4.3 路由别名
{
  path: '/dashboard',
  component: Dashboard,
  alias: '/dash', // '/dash' 和 '/dashboard' 映射到同一组件
},
4.4 嵌套路由
{
  path: '/settings',
  component: SettingsLayout,
  children: [
    { path: 'profile', component: ProfileSettings },
    { path: 'security', component: SecuritySettings },
  ],
}
// URL: /settings/profile → 渲染 SettingsLayout 和 ProfileSettings
4.5 路由元数据与守卫
{
  path: '/admin',
  component: AdminDashboard,
  meta: { requiresAuth: true }, // 自定义权限标记
  beforeEnter: (to, from, next) => {
    if (!userIsLoggedIn) next('/login');
    else next();
  },
}

5. 路由配置的完整流程

  1. 定义路由数组:用 RouteRecordRaw 对象描述所有路由。
  2. 创建路由实例
   const router = createRouter({
     history: createWebHistory(),
     routes, // 路由配置数组
   });
  1. 挂载到 Vue 应用
   createApp(App).use(router).mount('#app');

6. 注意事项

  • 组件导入方式
  • 直接导入import Component from './Component.vue'(适合小应用)。
  • 懒加载() => import('./Component.vue')(优化初始加载性能)。
  • 路径匹配规则
  • ^ 开头和 $ 结尾,确保精确匹配。
  • 动态参数需以 : 开头(如 :id)。
  • 404 页面
  {
    path: '/:pathMatch(.*)*', // 匹配所有未定义的路径
    component: NotFound,
  }

7. 典型场景

  • 动态路由:根据用户角色动态生成路由(如 /admin/*)。
  • 权限控制:通过 meta.requiresAuth 和全局守卫控制访问。
  • 多级导航:嵌套路由实现复杂页面结构(如仪表盘 → 子模块)。

总结

  • RouteRecordRaw 是 Vue Router 路由配置的核心接口,定义了路径与组件的映射关系。
  • 通过 path, component, children 等属性实现路由的灵活配置。
  • 结合 meta, redirect, beforeEnter 等高级属性,可实现权限控制、动态跳转等复杂逻辑。

创建 Router 实例

使用 createRoutercreateWebHistory 方法创建路由实例:

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// 继续 src/router/index.ts
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL), // HTML5 History 模式
  routes, // 路由配置数组
});
  • history:选择路由模式(createWebHistory 为 HTML5 History 模式,createWebHashHistory 为 hash 模式)。
  • routes:传递之前定义的路由配置数组。

Vite公开了环境对象Meta,其中包含一个BASE_URL属性。您可以在专用环境文件中设置BASE_URL的值,通常以.env前缀表示,或者在运行Vite服务器时通过命令行设置。然后Vite提取BASE_URL的相关值并将其注入到import.Meta.env对象中,我们可以在代码中使用它.

您不必在.env文件中为开发设置BASE_URL值。Vite会自动将其映射到本地服务器URL。大多数现代托管平台(如Netlify)会在部署过程中为您设置BASE_URL值,通常为应用程序的域名。

完整代码示例

src/router/index.ts:

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

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    component: Home,
    name: 'Home',
    meta: { title: '首页' },
  },
  {
    path: '/about',
    component: About,
    name: 'About',
  },
  {
    path: '/user/:id',
    component: () => import('@/views/User.vue'),
    name: 'User',
  },
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
});

export default router;

挂载到 Vue 应用

在主文件中引入并挂载路由实例,在初始化应用程序实例app的main.ts文件中,我们将导入创建的路由器实例,并将其作为参数传递给app.use()方法:

// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App);
app.use(router); // 挂载路由
app.mount('#app');

<RouterView><RouterLink>

1. <RouterView>:路由出口组件

<RouterView> 是 Vue Router 的核心组件,用于渲染匹配到的路由组件。每个 <RouterView> 对应一个路由层级,支持嵌套路由和多视图。


核心用法
<!-- App.vue -->
<template>
  <div id="app">
    <nav>
      <RouterLink to="/">首页</RouterLink>
      <RouterLink to="/about">关于</RouterLink>
    </nav>
    <RouterView /> <!-- 渲染匹配的路由组件 -->
  </div>
</template>

关键特性
特性说明
嵌套路由支持多层 <RouterView> 渲染子路由组件(如仪表盘的子页面)。
命名视图通过 name 属性同时渲染多个视图(需路由配置 components)。

嵌套路由示例
<!-- 父路由组件 Parent.vue -->
<template>
  <div>
    <h2>父组件内容</h2>
    <RouterView /> <!-- 渲染子路由组件 -->
  </div>
</template>
// 路由配置
const routes = [
  {
    path: '/parent',
    component: Parent,
    children: [
      { path: 'child1', component: Child1 },
      { path: 'child2', component: Child2 },
    ],
  },
];

命名视图示例
<!-- App.vue -->
<template>
  <RouterView /> <!-- 默认视图 -->
  <RouterView name="sidebar" /> <!-- 命名视图 "sidebar" -->
</template>
// 路由配置
{
  path: '/home',
  components: {
    default: HomeMain,
    sidebar: HomeSidebar,
  },
},

2. <RouterLink>:声明式导航组件

<RouterLink> 是 Vue Router 提供的声明式导航组件,替代原生 <a> 标签实现无页面刷新跳转。


基础用法
<RouterLink to="/about">关于</RouterLink>

关键属性
属性名类型说明
tostring/Object目标路由路径(如 /about)或对象(如 { name: 'User', params: { id: '123' } })。
replaceboolean是否替换历史记录(类似 router.replace())。
active-classstring指定激活链接的类名(默认 router-link-active)。
exactboolean是否精确匹配路径(如 /about 匹配 /about,但不匹配 /about/me)。
动态跳转示例
<RouterLink :to="{ name: 'User', params: { id: userId } }">用户详情</RouterLink>

编程式导航替代
<template>
  <button @click="goToAbout">跳转到关于页</button>
</template>

<script setup>
import { useRouter } from 'vue-router';

const router = useRouter();
const goToAbout = () => {
  router.push('/about');
};
</script>

router-link-activerouter-link-exact-active

这两个 CSS 类由 <RouterLink> 自动添加,用于高亮当前激活的链接。它们的区别在于匹配路径的严格程度

类名说明
router-link-active当链接路径与当前路由部分匹配时生效(如 /user/123 匹配 /user)。
router-link-exact-active当链接路径与当前路由完全匹配时生效(如 /user 匹配 /user,但不匹配 /user/123)。
使用 custom 属性和 v-slot

默认情况下,<RouterLink> 渲染为 <a> 标签。若需将其渲染为其他元素(如 <button>),可通过以下方法实现:

<template>
  <!-- 自定义为按钮 -->
  <RouterLink
    :to="{ name: 'About' }"
    custom
    v-slot="{ navigate, href, isExactActive }"
  >
    <button
      :class="{ 'active': isExactActive }"
      :href="href"
      @click="navigate"
    >
      关于页面
    </button>
  </RouterLink>
</template>
  • custom + v-slot 是自定义 <RouterLink> 渲染的核心方法。
  • 通过插槽暴露的 navigate 函数实现路由跳转,结合 isExactActive 等属性控制样式。
  • 适用于按钮、自定义图标、SVG 等非 <a> 元素的场景,同时保持路由功能。

通过此方法,可完全控制 <RouterLink> 的渲染形式,同时保留 Vue Router 的导航逻辑和激活状态管理。

路由间传递数据

在 Vue Router 中,在路由之间传递数据可以通过多种方式实现,具体选择取决于数据的类型和需求。以下是常见的方法及示例:


1. 通过 params(路径参数)

适用场景:传递动态路径参数(如用户 ID、文章 ID 等)。

步骤

  1. 定义路由路径:在路由配置中使用 :paramName 定义动态参数。
  2. 跳转时传递参数:通过 params 传递数据。
  3. 接收参数:在目标组件中通过 useRoute()this.$route 获取。

示例

// 路由配置(router/index.ts)
const routes = [
  {
    path: '/user/:id',
    component: UserDetail,
    name: 'UserDetail',
  },
];

// 跳转(使用编程式导航)
router.push({
  name: 'UserDetail',
  params: { id: '123' }, // 传递参数
});

// 接收参数(UserDetail.vue)
<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();
const userId = route.params.id as string; // 获取参数
</script>

2. 通过 query(查询参数)

适用场景:传递非路径相关的数据(如搜索条件、分页参数等)。

步骤

  1. 跳转时传递查询参数:通过 query 对象传递。
  2. 接收参数:在目标组件中通过 route.query 获取。

示例

// 跳转(编程式导航)
router.push({
  path: '/search',
  query: { q: 'vue', page: '1' }, // 传递查询参数
});

// 接收参数(Search.vue)
<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();
const searchQuery = route.query.q as string; // 'vue'
const pageNumber = Number(route.query.page); // 1
</script>

3. 通过 props(路由配置中声明 props)

适用场景:将路由参数或查询参数直接传递给目标组件作为 props

步骤

  1. 在路由配置中启用 props
  • props: true:将所有 params 作为 props 传递。
  • props: (route) => ({ ... }):自定义传递的 props。

示例

// 路由配置
{
  path: '/user/:id',
  component: UserDetail,
  props: true, // 启用 params → props
},

// 目标组件(UserDetail.vue)
<script setup>
const props = defineProps<{
  id: string;
}>();
</script>

4. 通过路由的 meta 字段

适用场景:传递元数据(如权限标记、标题等),不通过 URL 传递。

步骤

  1. 在路由配置中定义 meta
  2. 在守卫或组件中读取:通过 route.meta 获取。

示例

// 路由配置
{
  path: '/admin',
  component: Admin,
  meta: { requiresAuth: true }, // 自定义元数据
},

// 在守卫中使用(全局守卫)
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth) {
    // 验证权限
  }
  next();
});

5. 通过状态管理(如 Pinia/Vuex)

适用场景:需要跨组件共享复杂数据(如用户信息、表单数据等)。

步骤

  1. 存储数据到状态管理:在跳转前将数据存入 store。
  2. 在目标组件中读取:直接从 store 中获取。

示例(Pinia)

// 存储数据(在跳转前)
const userStore = useUserStore();
userStore.setUserData({ id: 123, name: 'Alice' });

// 跳转
router.push('/user/profile');

// 目标组件中读取
<script setup>
const userStore = useUserStore();
const userData = userStore.userData;
</script>

6. 通过路由守卫传递数据

适用场景:在导航过程中预加载数据并传递给目标组件。

步骤

  1. 在守卫中获取数据:在 beforeEachbeforeResolve 中获取数据。
  2. 将数据挂载到路由对象:通过 route.meta 或其他方式传递。

示例

// 全局守卫
router.beforeEach(async (to, from, next) => {
  if (to.params.id) {
    const data = await fetchData(to.params.id as string);
    to.meta.user = data; // 将数据挂载到 route.meta
  }
  next();
});

// 目标组件中读取
<script setup>
import { useRoute } from 'vue-router';

const route = useRoute();
const userData = route.meta.user; // 获取守卫中传递的数据
</script>

选择方法的建议

方法适用场景优点缺点
params/query需要通过 URL 传递数据(如路径参数或过滤条件)简单直接,支持浏览器历史记录和书签。数据暴露在 URL 中,可能不适合敏感信息。
props需要将路由参数直接映射到组件 props简化组件数据传递。仅适用于简单数据,需在路由配置中声明。
meta 字段传递元数据(如权限、标题)轻量且直接。仅限于路由配置中定义的数据,无法动态更新。
状态管理(Pinia)需要跨组件共享复杂数据或长期存储可维护性强,支持复杂逻辑。需要额外配置 store。
路由守卫需要在导航过程中预加载数据灵活控制数据加载流程。代码分散,可能增加复杂度。

注意事项

  1. 敏感数据:避免通过 paramsquery 传递敏感信息(如密码、令牌)。
  2. 类型安全:在 TypeScript 中使用 route.params 时,需显式类型断言(如 as string)。
  3. 性能优化:对于大量数据,优先使用状态管理而非 URL 参数。

通过以上方法,可以灵活地在 Vue Router 的路由间传递数据,选择合适的方式取决于具体需求和场景。

路由守卫

1. router.beforeEach

作用:在导航被确认之前(但在异步路由组件加载之前)触发,是最常用的守卫,用于全局拦截导航。


核心参数
  • to:目标路由对象(即将跳转到的路由)。
  • from:当前路由对象(当前所在的路由)。
  • next :必须调用此函数以继续导航:
  • next():继续导航。
  • next(false):取消导航。
  • next('/path'):重定向到其他路径。

典型用例
  • 权限验证:检查用户是否登录。
  • 设置页面标题:根据路由 meta 设置标题。
  • 加载状态控制:显示加载动画。

示例代码
router.beforeEach(async (to, from, next) => {
  // 1. 检查登录状态
  const isAuthenticated = await checkAuth();
  if (to.meta.requiresAuth && !isAuthenticated) {
    next('/login');
    return;
  }

  // 2. 设置页面标题
  document.title = to.meta.title || '默认标题';

  // 3. 允许导航
  next();
});

2. router.beforeResolve

作用:在导航被确认且所有异步路由组件加载完成后触发,但在实际渲染之前
适用场景:需要在路由组件完全加载后执行操作(如数据预加载)。


典型用例
  • 数据预加载:确保异步组件数据加载完成后再渲染。
  • 依赖注入:在渲染前注入全局数据。

示例代码
router.beforeResolve((to, from, next) => {
  // 确保异步组件已加载
  if (to.meta.loadData) {
    // 预加载数据
    loadDataForRoute(to).then(() => {
      next();
    });
  } else {
    next();
  }
});

3. router.afterEach

作用:在导航完成(即路由已更新,组件已渲染)后触发。
适用场景:导航后的清理操作、埋点统计、滚动位置恢复等。


典型用例
  • 滚动位置恢复:跳转后回到顶部或指定位置。
  • 埋点统计:记录页面访问数据。
  • 资源释放:清理临时数据。

示例代码
router.afterEach((to, from) => {
  // 1. 滚动到顶部
  window.scrollTo(0, 0);

  // 2. 埋点统计(如 Google Analytics)
  ga('send', 'pageview', to.fullPath);

  // 无需调用 next()
});

关键区别对比

守卫触发时机是否需要 next()典型用途
beforeEach导航确认前(异步组件加载前)必须权限验证、标题设置、加载状态控制
beforeResolve导航确认后,所有异步组件加载完成但渲染前可选数据预加载、依赖注入
afterEach导航完成(路由已更新,组件已渲染)无需滚动位置恢复、埋点统计、资源清理

常见问题与解决方案

问题 1:如何避免无限循环?

  • 场景:在 beforeEach 中调用 next('/login'),但 /login 路由又触发了相同的守卫。
  • 解决 :添加条件判断:
  if (to.path === '/login') {
    next();
    return;
  }

问题 2:异步操作如何处理?

  • 示例 :在beforeEach中进行异步登录检查:
  router.beforeEach(async (to, from, next) => {
    const isAuthenticated = await checkAuth();
    if (!isAuthenticated) {
      next('/login');
    } else {
      next();
    }
  });

问题 3:如何动态设置页面标题?

  • 方法 :在路由的meta字段定义标题,守卫中读取:
  typescript

  // 路由配置
  {
    path: '/about',
    component: About,
    meta: { title: '关于我们' },
  }

  // beforeEach 中
  document.title = to.meta.title || '默认标题';

完整示例代码

// src/router/index.ts
import { createRouter, createWebHistory, Router } from 'vue-router';
import { checkAuth } from '@/utils/auth';

const router: Router = createRouter({
  history: createWebHistory(),
  routes: [...],
});

// 1. beforeEach: 权限验证和标题设置
router.beforeEach(async (to, from, next) => {
  const isAuthenticated = await checkAuth();
  if (to.meta.requiresAuth && !isAuthenticated) {
    next('/login');
    return;
  }

  document.title = to.meta.title || '默认标题';
  next();
});

// 2. beforeResolve: 预加载数据
router.beforeResolve((to, from, next) => {
  if (to.meta.loadData) {
    loadDataForRoute(to).then(() => {
      next();
    });
  } else {
    next();
  }
});

// 3. afterEach: 滚动到顶部和埋点
router.afterEach((to, from) => {
  window.scrollTo(0, 0);
  ga('send', 'pageview', to.fullPath);
});

export default router;

总结

  • beforeEach:全局拦截导航,用于权限、标题、加载状态。
  • beforeResolve:在异步组件加载完成后执行,适合预加载数据。
  • afterEach:导航完成后清理或统计,无需控制流程。 通过合理使用这三个守卫,可以实现复杂的导航逻辑,提升应用的健壮性和用户体验。

router.back/forward

<template>
<button @click="router.back()">Back</button>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
</script>
<template>
<button @click="router.forward()">Forward</button>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
</script>

你也可以使用router.go()方法,它接受一个参数作为历史堆栈中要返回或前进的步骤数。例如,router.go(-2)将导航到后退两步的页面,而router.go(2)将向前跳转两步(如果它们存在)

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇