4、页面与布局:pages页面创建、layouts布局系统、error页面定制、app.vue入口文件
好,咱们今天聊聊 Nuxt3 里最核心的几个概念——页面、布局、错误处理和入口文件。说实话,这几个东西你搞明白了,整个项目的骨架基本就搭起来了。我刚开始用 Nuxt 的时候,也在这上面踩过不少坑,今天一并分享给你。
4.1 pages 页面创建——约定大于配置
Nuxt3 的页面系统,说白了就是「文件即路由」。你在 pages/ 目录下放一个 about.vue,它就自动生成了 /about 路由。这个设计我个人非常喜欢,省去了手动配路由的麻烦。
核心规则:pages 目录下的每个 .vue 文件,自动映射为一个路由。
来看个实际例子:
pages/
├── index.vue → /
├── about.vue → /about
├── blog/
│ ├── index.vue → /blog
│ └── [id].vue → /blog/:id
└── user/
└── [id]/
└── profile.vue → /user/:id/profile
嗯,这里要注意动态路由的写法。用方括号 [param] 表示动态参数,在页面里通过 useRoute() 获取:
<!-- pages/blog/[id].vue -->
<script setup>
const route = useRoute()
console.log(route.params.id) // 比如 /blog/123 就输出 123
</script>
我在项目中遇到过一个问题:嵌套路由的页面刷新后 404。后来发现是文件名大小写搞错了。Nuxt3 对大小写敏感,[Id].vue 和 [id].vue 是两个不同的路由。建议统一用小写。
4.2 layouts 布局系统——复用页面结构
布局系统解决的是「多个页面共享相同结构」的问题。比如每个页面都有导航栏和页脚,你总不能在每个页面都写一遍吧?
Nuxt3 的布局文件放在 layouts/ 目录下,默认布局是 default.vue:
<!-- layouts/default.vue -->
<template>
<div>
<header>网站导航栏</header>
<slot /> <!-- 页面内容会渲染到这里 -->
<footer>版权信息</footer>
</div>
</template>
页面里怎么用?很简单,页面组件不需要任何额外配置,默认就会套用 default.vue 布局。如果你想用其他布局,在页面里指定:
<!-- pages/login.vue -->
<script setup>
definePageMeta({
layout: 'auth' // 使用 layouts/auth.vue
})
</script>
小技巧:我习惯把布局按功能拆分。比如后台管理用 admin.vue,前台展示用 default.vue,登录注册用 auth.vue。这样每个布局职责清晰,维护起来也方便。
布局系统还有一个隐藏功能——嵌套布局。你可以在一个布局里引用另一个布局:
<!-- layouts/admin.vue -->
<template>
<div class="admin-layout">
<aside>侧边栏</aside>
<main>
<slot />
</main>
</div>
</template>
然后页面里用 definePageMeta({ layout: 'admin' }) 即可。注意,布局文件不要放在 layouts/ 的子目录里,Nuxt3 只识别一级目录下的布局文件。
4.3 error 页面定制——优雅处理异常
用户访问不存在的页面,或者服务端渲染报错了,总不能给用户看一个白屏吧?Nuxt3 允许你定制错误页面,放在项目根目录的 error.vue 里。
我曾经犯过一个错误:把 error.vue 放在了 pages 目录下。结果死活不生效。后来才明白,error.vue 是顶层文件,和 pages、layouts 平级。
<!-- error.vue(项目根目录) -->
<script setup>
const props = defineProps({
error: Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
<template>
<div class="error-page">
<h1>{{ error.statusCode }}</h1>
<p>{{ error.statusMessage }}</p>
<button @click="handleError">返回首页</button>
</div>
</template>
error 对象包含三个关键属性:
| 属性 | 说明 | 示例值 |
|---|---|---|
| statusCode | HTTP 状态码 | 404、500 |
| statusMessage | 状态描述 | "Not Found" |
| message | 详细错误信息 | "Page not found" |
注意:error.vue 本身不能使用布局系统。因为它是在布局之外渲染的。如果你需要样式,直接在 error.vue 里写内联样式或引入全局 CSS 即可。
你还可以在页面里手动触发错误:
<script setup>
const { data } = await useFetch('/api/something')
if (!data.value) {
throw createError({
statusCode: 404,
statusMessage: '数据不存在'
})
}
</script>
嗯,这里有个细节:createError 在服务端会触发 500 错误页面,在客户端则跳转到错误页面。所以建议在 useFetch 的 onError 回调里处理,而不是直接 throw。
4.4 app.vue 入口文件——应用的起点
app.vue 是 Nuxt3 应用的根组件。它和 Vue 3 的 App.vue 类似,但有一些 Nuxt 特有的功能。
默认的 app.vue 很简单:
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
这里 <NuxtLayout> 负责加载布局,<NuxtPage> 负责渲染当前路由对应的页面。你可以在 app.vue 里做全局的事情:
- 引入全局 CSS 或字体
- 设置页面过渡动画
- 添加全局的 loading 状态
- 注入全局的插件或组件
举个例子,我习惯在 app.vue 里做页面切换的过渡动画:
<template>
<NuxtLayout>
<NuxtPage :transition="{
name: 'page',
mode: 'out-in'
}" />
</NuxtLayout>
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: opacity 0.3s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
}
</style>
个人建议:app.vue 里不要写太多业务逻辑。它就像房子的地基,越干净越好。全局的请求拦截、状态管理,放到 plugins 或 middleware 里更合适。
还有一个容易忽略的点:如果你删除了 app.vue,Nuxt3 会自动使用默认的入口文件。但如果你创建了 app.vue,就必须包含 <NuxtLayout> 和 <NuxtPage>,否则页面渲染不出来。我曾经删掉 <NuxtLayout> 想偷懒,结果页面空白了半天才反应过来。
好了,这四个概念你掌握了吗?说白了就是:pages 管路由,layouts 管结构,error.vue 管异常,app.vue 管全局。把这四个文件的关系理清楚,Nuxt3 项目的骨架你就拿捏了。