Skip to content

页面与布局

Nuxt 提供了 <NuxtPage><NuxtLayout> 两个核心组件,构成了页面路由与布局系统的基础。

NuxtPage

渲染当前路由对应的页面组件,必须在 app.vue 或布局组件中使用。

vue
<!-- app/app.vue -->
<template>
  <NuxtRouteAnnouncer />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

TIP

如果没有 <NuxtPage> 路由页面将不会被渲染。这是 Nuxt 应用最基础的必需组件

Props

Prop类型默认值说明
pageKeystring | function-页面 key,控制何时重新渲染
transitionobject-页面过渡动画配置
keepaliveboolean | objectfalse缓存页面组件实例

pageKey 控制页面重渲染

默认情况下,同一路由的不同参数(如 /user/1/user/2)会复用组件实例。如果需要强制重新创建,可设置 pageKey

vue
<template>
  <!-- 根据完整路径重新渲染 -->
  <NuxtPage :page-key="route => route.fullPath" />

  <!-- 使用路由参数作为 key -->
  <NuxtPage :page-key="$route.params.id" />

  <!-- 固定 key(调试用,每次导航都重新渲染) -->
  <NuxtPage page-key="always-new" />
</template>

INFO

pageKey 的变化会导致组件完全销毁并重建 包括重置所有状态。请根据实际需求选择是否使用

页面过渡动画

vue
<!-- app/app.vue -->
<template>
  <NuxtPage :transition="{ name: 'fade', mode: 'out-in' }" />
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

也可以在每个页面中通过 definePageMeta 单独设置:

vue
<!-- app/pages/about.vue -->
<script setup>
definePageMeta({
  pageTransition: { name: 'slide', mode: 'out-in' },
})
</script>

keepalive 缓存页面

vue
<template>
  <!-- 缓存所有页面 -->
  <NuxtPage :keepalive="true" />

  <!-- 仅缓存指定组件 -->
  <NuxtPage :keepalive="{ include: ['HomePage', 'DashboardPage'] }" />

  <!-- 排除某些组件 -->
  <NuxtPage :keepalive="{ exclude: ['FormPage'] }" />
</template>

TIP

使用 keepalive 后 页面切换时组件不会被销毁,onMounted 只触发一次,onActivated / onDeactivated 在每次切换时触发

NuxtPage 的插槽

vue
<template>
  <NuxtPage>
    <!-- 获取当前页面的组件引用 -->
    <template #default="pageComponent">
      <component :is="pageComponent" />
    </template>
  </NuxtPage>
</template>

NuxtLayout

渲染布局组件,包裹页面内容提供统一的页面结构(如导航栏、侧边栏、页脚)。

vue
<!-- app.vue -->
<template>
  <NuxtRouteAnnouncer />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Props

Prop类型默认值说明
namestring'default'布局名称
fallbackstring-后备布局(指定布局不存在时使用)

动态切换布局

vue
<template>
  <!-- 使用指定布局 -->
  <NuxtLayout name="admin">
    <NuxtPage />
  </NuxtLayout>

  <!-- 动态布局 -->
  <NuxtLayout :name="isAdmin ? 'admin' : 'default'">
    <NuxtPage />
  </NuxtLayout>
</template>

<script setup>
const route = useRoute()
const isAdmin = computed(() => route.path.startsWith('/admin'))
</script>

通过页面元信息指定布局

更常见的做法是在页面中通过 definePageMeta 指定布局:

vue
<!-- app/pages/dashboard.vue -->
<script setup>
definePageMeta({
  layout: 'admin',
})
</script>

<template>
  <div>Dashboard 内容</div>
</template>

布局 Props(v4.4+)

通过 definePageMeta 传递 Props 给布局:

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

<template>
  <div class="panel-layout">
    <aside v-if="sidebar">侧边栏</aside>
    <main>
      <header v-if="title">{{ title }}</header>
      <slot />
    </main>
  </div>
</template>

fallback 布局

vue
<template>
  <!-- 如果 'custom' 布局不存在,使用 'default' -->
  <NuxtLayout name="custom" fallback="default">
    <NuxtPage />
  </NuxtLayout>
</template>

TIP

布局文件位于 app/layouts/ 目录 默认布局为 app/layouts/default.vue。如果没有自定义布局,<NuxtLayout> 会直接渲染 <slot />

常见问题

问题原因解决方案
页面不渲染缺少 <NuxtPage>确保在 app.vue 中添加
布局不生效页面未指定布局使用 definePageMeta({ layout: 'xxx' })
过渡动画不生效缺少 CSS添加对应的过渡 CSS 样式
keepalive 无效组件名不匹配确保组件名与 include/exclude 列表匹配

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