路由助手

ubean 的路由辅助能力围绕 useRouter()(来自 vue-router;仅在启用 autoImports: { vueRouter: true } 时才自动导入,默认关闭)与全局注册的 <Link> 组件展开。ubean 不提供 useRoute()、navigateTo()、redirectTo()、useRouteParams() 或 useRouteQuery()——请改用 router.currentRoute(见下文)或 vue-router 的 useRoute()。i18n 路径助手(useLocalePath、useSwitchLocalePath)见 i18n 参考。

useRouter()

useRouter() 返回扩展了 push/replace 快捷方法的 Vue Router 实例。在客户端组件中从 vue-router 导入(自动导入需要 autoImports.vueRouter,默认关闭):

<script setup lang="ts">
const router = useRouter();
</script>

方法

方法说明
push(to)导航到新路由
replace(to)替换当前路由(不产生历史记录)
back()后退一步
forward()前进一步
go(n)前进 n 步(负数为后退)
beforeEach注册全局 beforeEach 守卫
afterEach注册全局 afterEach 钩子

读取当前路由

通过 router.currentRoute.value 查看当前路由:

const router = useRouter();
const route = router.currentRoute.value;

console.log(route.path);      // "/users/123"
console.log(route.params.id); // "123"
console.log(route.query.q);   // "search term"
console.log(route.hash);      // "#section"
console.log(route.fullPath);  // "/users/123?q=...#section"
console.log(route.name);      // "UserDetail"
console.log(route.meta);      // 页面元数据

若要在模板中响应式读取,直接用 router.currentRoute,或用 computed 解包:

<script setup lang="ts">
import { computed } from 'vue';

const router = useRouter();
const currentPath = computed(() => router.currentRoute.value.path);
const userId = computed(() => router.currentRoute.value.params.id as string);
</script>

<template>
  <p>Path: {{ currentPath }}</p>
  <p>User: {{ userId }}</p>
</template>

编程式导航

const router = useRouter();

// 字符串路径
router.push('/about');

// 带 name + params 的对象形式
router.push({ name: 'UserDetail', params: { id: '123' } });

// 带 query
router.push({ path: '/search', query: { q: 'ubean' } });

// 带 hash
router.push({ path: '/docs', hash: '#section-1' });

// 替换(不产生历史记录)
router.replace('/login');

// 后退 / 前进
router.back();
router.forward();
router.go(-2);

导航守卫

注册导航守卫有两种方式:

1. 通过 defineApp({ router }) 注册全局守卫 —— 推荐

在 src/app.ts 中于应用启动时注册一次。守卫在客户端与 SSR 都会执行,并且能拦截首次导航。详见 导航守卫指南。

// src/app.ts
import { defineApp } from 'ubean';

export default defineApp({
  router: {
    setup(router) {
      router.beforeEach((to, from) => {
        if (to.meta.requiresAuth && !isAuthenticated()) {
          return '/login';
        }
      });
      router.afterEach((to, from) => {
        // 埋点、回到顶部等
      });
    }
  }
});

2. 通过 useRouter() 注册组件级守卫

适用于组件作用域内的守卫(较少用;主要在长期存活的根组件里)。每次调用都会追加一个守卫——注意不要在每次挂载时重复注册:

const router = useRouter();

router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return '/login';
  }
});

router.afterEach((to, from) => {
  // 埋点、回到顶部等
});

生产环境的鉴权/埋点守卫应放在 defineApp({ router }) 中,以避免重复注册,并确保它们在 SSR 期间也会执行。

<Link> 组件(全局)

<Link> 已全局注册——无需导入。它执行客户端导航,并支持激活态样式。

<template>
  <!-- 字符串路径 -->
  <Link to="/about">About</Link>

  <!-- 带 params 的具名路由 -->
  <Link :to="{ name: 'UserDetail', params: { id: '123' } }">User</Link>

  <!-- 带 query -->
  <Link :to="{ path: '/search', query: { q: 'ubean' } }">Search</Link>
</template>

Props

Prop类型说明
tostring | { name, params, query, hash }目标路由
localestring把目标路径本地化到指定 locale(i18n)
replaceboolean使用 router.replace 而非 push
hrefstring覆盖渲染出的 href
prefetchboolean预取目标页面的 chunk
activeClassstring链接匹配当前路由时应用的类
exactActiveClassstring精确匹配时应用的类
noActiveClassboolean禁用默认激活态类

外部 URL(以 http://、https://、// 开头)会被自动识别,渲染为普通 <a> 标签并带上 target="_blank" rel="noopener noreferrer"。

插槽作用域

默认插槽暴露 isActive 与 isExactActive:

<template>
  <Link to="/about" v-slot="{ isActive }">
    <span :class="{ active: isActive }">About</span>
  </Link>
</template>

页面元数据(definePage)

在 <script setup> 中用 definePage 宏设置页面元数据。它是编译时宏,已自动导入:

<script setup lang="ts">
definePage({
  name: 'About',
  path: '/about',                    // 覆盖自动生成的路径
  layout: 'default',
  meta: {
    title: 'About Page',
    description: 'About our company'
  },
  requiresAuth: true,
  cache: true,                       // 开启 KeepAlive 页面缓存
  head: {
    title: 'About',
    meta: [{ name: 'description', content: 'About us' }]
  }
});
</script>

字段

字段类型说明
namestring路由名(建议 PascalCase)
pathstring覆盖自动生成的 URL 路径
layoutstring | string[] | false布局名、由外到内的布局链,或 false 表示禁用
reusestring复用路由的目标
metaobject自定义元数据(任意结构)
requiresAuthboolean鉴权要求(meta 简写)
cacheboolean开启 KeepAlive 页面缓存
headobject页面级 head 标签(@unhead/vue)
ssrboolean | 'streaming' | 'data-only'页面级渲染覆盖
transitionstring页面过渡名(空字符串表示禁用)

没有顶层 title 字段——请用 meta: { title } 或 head: { title }。 没有顶层 middleware 字段——路由级中间件通过 meta: { middleware: [...] } 声明。

页面缓存(cache: true)

设置 cache: true 后,离开该页面时会用 Vue 的 <KeepAlive> 保留页面组件实例。页面路由名(如 'About')即缓存键——框架会自动用带名字的包装组件(getNamedPageWrapper)包裹页面组件,因此 <script setup> SFC 无需手动调用 defineOptions({ name })。

被缓存时,页面组件上触发的是 onActivated / onDeactivated 生命周期钩子(而非 onMounted / onUnmounted):

<script setup lang="ts">
import { onActivated, onDeactivated } from 'vue';

definePage({ cache: true });

onActivated(() => {
  console.log('Page re-activated (navigated back)');
});

onDeactivated(() => {
  console.log('Page deactivated (navigated away, but kept alive)');
});
</script>

运行时控制可用 useCacheViews() / enablePageCache(name) / disablePageCache(name) / excludePageCache(name) / invalidatePageCache(name)(从 ubean/client 自动导入)。

reuse 路由的缓存继承

.reuse.ts 页面若未显式声明 cache,就会继承目标页面的 cache 设置。你也可以在 reuse 路由上显式开关缓存,与目标页面无关:

// pages/about.vue —— 开启缓存
definePage({ cache: true });

// pages/about2.reuse.ts —— 继承 About 的 cache: true(无需重复声明)
definePage({ reuse: 'About' });

// pages/about3.reuse.ts —— 显式关闭缓存(覆盖继承值)
definePage({ reuse: 'About', cache: false });

// pages/about4.reuse.ts —— 即使目标未缓存也显式开启缓存
definePage({ reuse: 'About', cache: true });

每个被缓存的 reuse 路由都是独立的 KeepAlive 实例,以自身的路由名作为键。

在 API 路由中读取路由参数

在 API 路由处理器(服务端)中,使用 Hono 的 c.req.param():

// src/routes/api/users/[id].ts
import { defineHandler } from 'ubean/server';

export const GET = defineHandler(c => {
  const id = c.req.param('id');
  return c.json({ id });
});

若需要类型化的参数,使用 validator('param', schema):

import { defineHandler, validator } from 'ubean/server';
import { z } from 'zod';

export const GET = defineHandler(
  validator('param', z.object({ id: z.string() })),
  c => {
    const { id } = c.req.valid('param');
    return c.json({ id });
  }
);