Skip to content

页面元信息

使用 definePageMeta 为页面添加元数据,控制页面行为。

为什么需要页面元信息?

不同的页面可能需要不同的配置:

需求不用 definePageMeta用 definePageMeta
某个页面用 admin 布局在页面内手动切换布局layout: 'admin'
某个页面需要登录在页面内写判断逻辑middleware: 'auth'
动态路由参数验证在页面内验证validate: ...
页面切换动画全局统一每个页面自定义

definePageMeta 让你在页面级别配置这些行为,而不是全局配置。

基本用法

vue
<script setup>
definePageMeta({
  layout: 'default',
  middleware: 'auth',
  title: '用户中心',
})
</script>

definePageMeta 的特殊性

  • 它在编译时被提取和处理,不在运行时执行
  • 这意味着你不能使用运行时变量
  • 它是一个编译器宏,不是普通函数

可用选项

layout — 指定布局

vue
<script setup>
definePageMeta({
  layout: 'admin',  // 使用 app/layouts/admin.vue
})

// 或禁用布局
definePageMeta({
  layout: false,  // 不使用任何布局
})
</script>

为什么有时要禁用布局?

登录页面通常不需要导航栏和侧边栏,设置 layout: false 后页面独立显示。

Nuxt 4 布局 Props

Nuxt 4.4+ 支持向布局传递 Props:

vue
<!-- pages/dashboard.vue -->
<script setup>
definePageMeta({
  layout: {
    name: 'panel',
    props: {
      sidebar: true,
      title: 'Dashboard',
    },
  },
})
</script>
vue
<!-- layouts/panel.vue -->
<script setup lang="ts">
defineProps<{
  sidebar?: boolean
  title?: string
}>()
</script>

为什么需要向布局传 Props?

以前布局是"一个模子刻出来的",所有使用同一布局的页面外观完全一样。现在你可以让不同页面在使用同一布局时,有不同的表现(如侧边栏是否显示)。

middleware — 路由中间件

vue
<script setup>
definePageMeta({
  // 单个中间件
  middleware: 'auth',

  // 多个中间件
  middleware: ['auth', 'admin'],

  // 内联中间件
  middleware: [
    (to, from) => {
      // 中间件逻辑
    },
  ],
})
</script>

validate — 路由验证

验证动态路由参数,验证失败返回 404:

vue
<script setup>
definePageMeta({
  validate: async (route) => {
    // id 必须是数字
    return /^\d+$/.test(route.params.id as string)
  },
})
</script>

验证流程

  1. 用户访问 /blog/abc
  2. validate 返回 false
  3. Nuxt 自动显示 404 页面

典型场景

  • ID 必须是数字:/^\d+$/.test(route.params.id)
  • slug 必须符合格式:/^[a-z0-9-]+$/.test(route.params.slug)
  • 文章是否存在:await checkPostExists(route.params.id)

pageTransition — 页面过渡

vue
<script setup>
definePageMeta({
  pageTransition: {
    name: 'slide-left',
    mode: 'out-in',
  },
})
</script>

layoutTransition — 布局过渡

vue
<script setup>
definePageMeta({
  layoutTransition: {
    name: 'fade',
    mode: 'out-in',
  },
})
</script>

key — 页面 key

控制页面组件是否重新渲染:

vue
<script setup>
definePageMeta({
  // 路由变化时完全重新渲染(不复用组件)
  key: route => route.fullPath,

  // 固定 key(所有实例共享同一组件)
  key: 'static',
})
</script>

什么时候需要设置 key?

  • 默认情况下,同一组件的路由会复用(如 /users/1/users/2
  • 如果你希望参数变化时完全重新渲染(而不是复用),设置 key: route => route.fullPath
  • 这会影响性能,只在需要时使用

keepalive — 缓存页面

vue
<script setup>
definePageMeta({
  keepalive: true,
  // 或带选项
  keepalive: {
    include: ['MyComponent'],
    max: 10,
  },
})
</script>

keepalive 的作用

缓存组件实例,离开页面后不销毁,回来时恢复状态。

适合场景

用户在列表页翻了 3 页,点进详情页,再回来时希望还在第 3 页。

INFO

注意keepalive 会保留组件状态 包括未清理的定时器和事件监听。可能导致内存泄漏,谨慎使用

自定义元信息

你可以添加任意自定义字段,在中间件中访问:

vue
<script setup>
definePageMeta({
  requiresAuth: true,
  roles: ['admin', 'editor'],
  breadcrumb: [
    { label: '首页', to: '/' },
    { label: '用户管理', to: '/users' },
    { label: '详情' },
  ],
})
</script>

在中间件中访问:

ts
export default defineNuxtRouteMiddleware((to) => {
  if (to.meta.requiresAuth) {
    // 需要认证
  }

  if (to.meta.roles?.includes('admin')) {
    // 需要管理员
  }
})

自定义元信息的灵活用途

  • requiresAuth:标记需要认证的页面
  • roles:标记需要特定角色
  • breadcrumb:面包屑导航数据
  • title:页面标题(给中间件用,统一设置 SEO)
  • activeMenu:当前激活的菜单项

类型扩展

为自定义元信息添加类型声明:

ts
// app/types/index.d.ts
declare module '#app' {
  interface PageMeta {
    requiresAuth?: boolean
    roles?: string[]
    breadcrumb?: Array<{ label: string; to?: string }>
  }
}

export {}

为什么要添加类型声明?

没有 export {} 的话,文件会被当作脚本而不是模块,declare module 会污染全局类型。

注意事项

1. 只能在页面中使用

definePageMeta 只能在 pages/ 下的 .vue 文件中使用。

2. 编译时转换

definePageMeta 在编译时被提取,不在运行时执行:

vue
<!-- ❌ 错误:不能使用运行时变量 -->
<script setup>
const myLayout = computed(() => isMobile.value ? 'mobile' : 'desktop')
definePageMeta({
  layout: myLayout,  // 不行,这不是编译时常量
})
</script>

<!-- ✅ 正确:在中间件中动态设置 -->
<script setup>
definePageMeta({
  middleware: 'set-layout',
})
</script>
ts
// app/middleware/set-layout.ts
export default defineNuxtRouteMiddleware((to) => {
  const isMobile = useIsMobile()
  setPageLayout(isMobile.value ? 'mobile' : 'default')
})

为什么不能使用运行时变量?

definePageMeta 的内容在编译时被提取到路由配置中。编译时没有 computedref 这些运行时概念,只有静态值。

如果你需要动态行为,使用中间件 + setPageLayout()

3. 不能访问组件上下文

definePageMeta 中不能使用 useRouteuseState 等组合式函数,因为它不在组件实例中执行。

知识脉络

text
路由规则 → 你在这里:页面元信息

              ├─→ 相关:布局(05-视图与布局)

              ├─→ 相关:路由中间件(04-路由与导航)

              └─→ 相关:页面过渡(05-视图与布局)

基于 Nuxt 4 官方文档整理编写