页面与布局
Nuxt 提供了 <NuxtPage> 和 <NuxtLayout> 两个核心组件,构成了页面路由与布局系统的基础。
NuxtPage
渲染当前路由对应的页面组件,必须在 app.vue 或布局组件中使用。
vue
<!-- app/app.vue -->
<template>
<NuxtRouteAnnouncer />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>TIP
如果没有 <NuxtPage> 路由页面将不会被渲染。这是 Nuxt 应用最基础的必需组件
Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
pageKey | string | function | - | 页面 key,控制何时重新渲染 |
transition | object | - | 页面过渡动画配置 |
keepalive | boolean | object | false | 缓存页面组件实例 |
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 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | string | 'default' | 布局名称 |
fallback | string | - | 后备布局(指定布局不存在时使用) |
动态切换布局
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 列表匹配 |