Nuxt 是一个直观的 Vue.js 全栈框架,提供自动路由、服务端渲染(SSR)、静态站点生成(SSG)以及经过优化的 Nitro 服务器引擎性能。
适用场景
- 全栈 Vue 3 Web 应用:Nuxt 3/4,支持通用 SSR、静态生成(SSG)和自动导入。
- 企业级 SEO 与快速加载页面:需要动态 meta 标签和即时渲染的营销站点与电商商店。
- 边缘就绪的服务器引擎(Nitro):将 Serverless 函数无缝部署到 Cloudflare、Vercel、Netlify 和 Node。
- 模块化前端架构:使用第一方模块(@nuxtjs/tailwindcss、@pinia/nuxt、@vueuse/nuxt)。
快速开始
Nuxt 会自动导入组件和组合式函数。
<script setup> // useFetch 已自动导入 const { data: quote } = await useFetch("/api/quote"); </script>
<template> <blockquote>{{ quote.text }}</blockquote> </template>
核心概念
使用 useFetch 与 useAsyncData 的通用数据获取
支持 SSR 的数据获取,并具备自动去重能力:
<!-- pages/products/[id].vue --> <script setup lang="ts"> interface Product { id: number; title: string; price: number; }
const route = useRoute(); const productId = route.params.id;
const {
data: product,
pending,
error,
} = await useFetch<Product>(/api/products/${productId}, {
key: product-${productId},
lazy: false,
});
useSeoMeta({
title: () =>
product.value?.title ? ${product.value.title} - Store : "Loading...",
description: () => Buy ${product.value?.title} online now.,
});
</script>
<template> <div class="product-page"> <div v-if="pending">Loading product details...</div> <div v-else-if="error">Error loading product: {{ error.message }}</div> <div v-else-if="product"> <h1>{{ product.title }}</h1> <p class="price">${{ product.price }}</p> </div> </div> </template>
Nitro 服务器引擎 API 路由
直接在 server/api 内创建后端 API 端点:
// server/api/products/[id].ts export default defineEventHandler(async (event) => { const id = getRouterParam(event, "id");
if (!id || isNaN(Number(id))) { throw createError({ statusCode: 400, statusMessage: "Invalid product ID", }); }
return {
id: Number(id),
title: Premium Mechanical Keyboard #${id},
price: 149.99,
};
});
Nuxt 中间件与路由守卫
客户端与服务端路由身份验证:
// middleware/auth.ts export default defineNuxtRouteMiddleware((to, from) => { const user = useCookie("auth_token");
if (!user.value && to.path.startsWith("/dashboard")) { return navigateTo("/login"); } });
常见模式
服务端 API 路由配合 useFetch 数据水合
问题:客户端获取数据会导致内容闪烁,并影响搜索引擎索引。
解决方案: 使用 Nuxt 服务端路由搭配通用 useFetch:
// server/api/products.ts export default defineEventHandler(async (event) => { return await db.products.findMany({ take: 20 }); });
// pages/products.vue <script setup> const { data: products, pending, error } = await useFetch('/api/products'); </script>
<template> <div v-if="pending">Loading products...</div> <ul v-else> <li v-for="p in products" :key="p.id">{{ p.name }} - ${{ p.price }}</li> </ul> </template>
最佳实践
推荐做法:
- 在 <script setup> 内使用 useFetch 进行顶层组件数据获取,以消除 SSR 双重获取。
- 利用 Nuxt 服务端 API 路由(server/api/)进行后端代理和密钥保护。
- 使用 useSeoMeta() 实现响应式、类型安全的 SEO 管理。
- 启用 Nitro 路由规则(routeRules)进行细粒度缓存和按页面的 ISR。
不推荐做法:
- 在 setup 脚本中直接使用标准 fetch();它会在服务端和客户端都执行,且没有水合传输。
- 在 onMounted 之外访问 window 或 document,或未检查 import.meta.client。
- 跨用户请求修改服务端状态。
故障排查
错误 | 原因 | 解决方案
window is not defined / document is not defined | 在服务端预渲染步骤中访问浏览器 DOM。 | 将浏览器代码包裹在 onMounted(() => { ... }) 或 <ClientOnly> 中。
[nuxt] [request error] [unhandled] 500 | 服务端路由抛出未处理异常。 | 检查终端控制台输出的服务端堆栈跟踪,并用 try/catch 包裹。
Component not found on auto-import | 嵌套文件夹结构要求组件名带路径前缀。 | 遵循命名约定:components/base/Button.vue -> <BaseButton />。
参考资料
Nuxt 文档