name: nuxt description: Nuxt 3 模式,包括 Nitro 服务器、自动导入、模块、混合渲染和全栈开发。 allowed-tools: Read, Write, Edit, Bash, Glob, Grep
Nuxt 技能
为使用 Nuxt 3 构建全栈应用程序提供专家级协助。
能力
- 使用 TypeScript 配置 Nuxt 3 项目
- 实现混合渲染(SSR、SSG、ISR、SPA)
- 构建 Nitro 服务器路由和中间件
- 使用自动导入创建组合式函数
- 设置 Nuxt 模块和插件
- 为各种平台配置部署
使用场景
在以下情况下调用此技能:
- 创建 Nuxt 3 全栈应用程序
- 实现服务器端渲染
- 使用 Nitro 构建 API 路由
- 配置渲染模式
- 部署 Nuxt 应用程序
输入参数
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| projectType | string | 是 | 项目类型:全栈、静态、单页应用 |
| rendering | string | 否 | 渲染模式:服务端渲染、静态生成、增量静态再生、单页应用 |
| modules | array | 否 | 要包含的 Nuxt 模块 |
| features | array | 否 | 功能:认证、国际化、渐进式网页应用等 |
配置示例
{
"projectType": "fullstack",
"rendering": "ssr",
"modules": ["@nuxtjs/tailwindcss", "@pinia/nuxt", "@vueuse/nuxt"],
"features": ["auth", "api"]
}
项目结构
nuxt-app/
├── .nuxt/ # 构建目录
├── assets/ # 未编译资源
├── components/ # 自动导入的组件
│ ├── base/
│ │ └── Button.vue # <BaseButton />
│ └── TheHeader.vue # <TheHeader />
├── composables/ # 自动导入的组合式函数
│ ├── useAuth.ts
│ └── useFetch.ts
├── layouts/ # 页面布局
│ ├── default.vue
│ └── auth.vue
├── middleware/ # 路由中间件
│ └── auth.ts
├── pages/ # 基于文件的路由
│ ├── index.vue # /
│ ├── about.vue # /about
│ └── users/
│ ├── index.vue # /users
│ └── [id].vue # /users/:id
├── plugins/ # Vue 插件
│ └── api.ts
├── public/ # 静态文件
├── server/ # Nitro 服务器
│ ├── api/
│ │ └── users/
│ │ ├── index.ts # /api/users
│ │ └── [id].ts # /api/users/:id
│ ├── middleware/
│ │ └── auth.ts
│ └── utils/
│ └── db.ts
├── stores/ # Pinia 状态管理
│ └── user.ts
├── nuxt.config.ts
└── app.vue
配置
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'@vueuse/nuxt',
],
runtimeConfig: {
// 仅服务器端
databaseUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
// 客户端可访问
public: {
apiBase: process.env.API_BASE || '/api',
},
},
routeRules: {
'/': { prerender: true },
'/api/**': { cors: true },
'/dashboard/**': { ssr: false },
'/blog/**': { isr: 3600 },
},
nitro: {
preset: 'vercel',
},
});
服务器 API 路由
// server/api/users/index.ts
export default defineEventHandler(async (event) => {
const method = event.method;
if (method === 'GET') {
const query = getQuery(event);
return await db.user.findMany({
where: query.search
? { name: { contains: query.search as string } }
: undefined,
});
}
if (method === 'POST') {
const body = await readBody(event);
return await db.user.create({ data: body });
}
throw createError({ statusCode: 405, message: 'Method not allowed' });
});
// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
const method = event.method;
if (method === 'GET') {
const user = await db.user.findUnique({ where: { id } });
if (!user) {
throw createError({ statusCode: 404, message: 'User not found' });
}
return user;
}
if (method === 'PUT') {
const body = await readBody(event);
return await db.user.update({ where: { id }, data: body });
}
if (method === 'DELETE') {
await db.user.delete({ where: { id } });
return { success: true };
}
});
服务器中间件
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const protectedPaths = ['/api/users', '/api/posts'];
const path = event.path;
if (protectedPaths.some((p) => path.startsWith(p))) {
const token = getHeader(event, 'authorization')?.replace('Bearer ', '');
if (!token) {
throw createError({ statusCode: 401, message: 'Unauthorized' });
}
try {
const user = await verifyToken(token);
event.context.user = user;
} catch {
throw createError({ statusCode: 401, message: 'Invalid token' });
}
}
});
页面和布局
<!-- pages/users/[id].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: user, pending, error } = await useFetch(
`/api/users/${route.params.id}`
);
useHead({
title: () => user.value?.name ?? 'User',
});
definePageMeta({
layout: 'default',
middleware: 'auth',
});
</script>
<template>
<div>
<div v-if="pending">加载中...</div>
<div v-else-if="error">{{ error.message }}</div>
<div v-else>
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</div>
</template>
<!-- layouts/default.vue -->
<template>
<div class="min-h-screen">
<TheHeader />
<main class="container mx-auto px-4 py-8">
<slot />
</main>
<TheFooter />
</div>
</template>
组合式函数
// composables/useAuth.ts
export const useAuth = () => {
const user = useState<User | null>('user', () => null);
const token = useCookie('auth-token');
const isAuthenticated = computed(() => !!user.value);
async function login(email: string, password: string) {
const { data } = await useFetch('/api/auth/login', {
method: 'POST',
body: { email, password },
});
if (data.value) {
user.value = data.value.user;
token.value = data.value.token;
}
}
async function logout() {
user.value = null;
token.value = null;
await navigateTo('/login');
}
async function fetchUser() {
if (!token.value) return;
const { data } = await useFetch('/api/auth/me', {
headers: { Authorization: `Bearer ${token.value}` },
});
user.value = data.value;
}
return {
user: readonly(user),
isAuthenticated,
login,
logout,
fetchUser,
};
};
中间件
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { isAuthenticated } = useAuth();
if (!isAuthenticated.value) {
return navigateTo({
path: '/login',
query: { redirect: to.fullPath },
});
}
});
// middleware/guest.ts
export default defineNuxtRouteMiddleware(() => {
const { isAuthenticated } = useAuth();
if (isAuthenticated.value) {
return navigateTo('/dashboard');
}
});
最佳实践
- 使用自动导入保持代码整洁
- 利用 Nitro 构建 API 路由
- 配置路由规则以实现最佳渲染
- 使用 useFetch/useAsyncData 进行数据获取
- 使用文件夹命名约定组织组件
目标流程
- nuxt-全栈开发
- vue-服务端渲染应用
- jamstack-开发
- api-开发