这是一个基于 Next.js 16 + shadcn/ui 的全栈应用项目,由扣子编程 CLI 创建。
pnpm dev默认在端口 5000 启动。可通过环境变量覆盖端口:
PORT=5173 pnpm dev启动后,在浏览器中打开对应地址(如 http://localhost:5000 或 http://localhost:5173)查看应用。
开发服务器支持热更新,修改代码后页面会自动刷新。
pnpm buildpnpm start默认端口为 5000,可通过环境变量覆盖:
DEPLOY_RUN_PORT=8080 pnpm start- Node.js 20+(工作流使用 Node 24,建议本地 18+)
- pnpm 9+
- 浏览器现代版本
pnpm install- 启动:
pnpm dev - 端口:默认
5000,可用PORT=<port>覆盖(脚本见 scripts/dev.sh) - 常用示例:
PORT=5173 pnpm dev
- 执行:
pnpm build - 产物目录:
./out - 预览静态产物:
npx serve ./out -p 8080 # 打开 http://localhost:8080 - 配置参考:next.config.ts
output: "export"images.unoptimized: true- 生产环境
assetPrefix: "./"适配 GitHub Pages 子路径
- 确认工作流文件已存在:.github/workflows/deploy.yml
- 触发:推送到
main或手动workflow_dispatch - 构建:
pnpm build,上传./out作为 Pages Artifact
- 触发:推送到
- 推送代码到 GitHub 仓库的
main分支 - 打开仓库 Settings → Pages → Source 选择 “GitHub Actions”
- 等待 Actions 完成,发布地址显示在 Pages 环境(如
https://<用户名>.github.io/<仓库名>/)
- 本地执行
pnpm build生成./out - 使用任意静态服务器托管
out(Nginx、Apache、Vercel 静态、Cloudflare Pages、Netlify 等) - 不建议将
out/目录提交到main分支(已在 .gitignore 忽略)
- 需要提交:
- 源码与配置:
src/、public/、scripts/、next.config.ts、package.json、pnpm-lock.yaml、tsconfig.json等 - 工作流:
.github/workflows/deploy.yml - 文档:
README.md、LICENSE(如有)
- 源码与配置:
- 不需要提交(已忽略):
node_modules/、.next/、out/、coverage/.DS_Store、.env*(敏感信息)- 详见 .gitignore
- 静态导出生成
out/,可部署到任何静态主机 - 不支持需 Node.js 服务器的特性:
- API Routes、Rewrites/Redirects、Headers、Proxy
- Incremental Static Regeneration(ISR)、Draft Mode
- 默认
next/image优化(需自定义 Loader) getServerSideProps、getStaticPaths的fallback: true/'blocking'
- 可用特性(静态场景):
getStaticProps、getStaticPaths(无fallback)- 动态路由的静态生成、
next/link预取、动态导入、任意样式方案
- 构建错误:
robots.txt需静态化- 已在 robots.ts 设置
export const dynamic = 'force-static'
- 已在 robots.ts 设置
- 子路径资源 404:生产已设置
assetPrefix: "./",确保通过 Actions 发布 - 开发端口占用:使用
PORT=<port> pnpm dev指定新端口 - 生产端口修改:使用
DEPLOY_RUN_PORT=<port> pnpm start
src/
├── app/ # Next.js App Router 目录
│ ├── layout.tsx # 根布局组件
│ ├── page.tsx # 首页
│ ├── globals.css # 全局样式(包含 shadcn 主题变量)
│ └── [route]/ # 其他路由页面
├── components/ # React 组件目录
│ └── ui/ # shadcn/ui 基础组件(优先使用)
│ ├── button.tsx
│ ├── card.tsx
│ └── ...
├── lib/ # 工具函数库
│ └── utils.ts # cn() 等工具函数
└── hooks/ # 自定义 React Hooks(可选)
优先使用 shadcn/ui 基础组件
本项目已预装完整的 shadcn/ui 组件库,位于 src/components/ui/ 目录。开发时应优先使用这些组件作为基础:
// ✅ 推荐:使用 shadcn 基础组件
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
export default function MyComponent() {
return (
<Card>
<CardHeader>标题</CardHeader>
<CardContent>
<Input placeholder="输入内容" />
<Button>提交</Button>
</CardContent>
</Card>
);
}可用的 shadcn 组件清单
- 表单:
button,input,textarea,select,checkbox,radio-group,switch,slider - 布局:
card,separator,tabs,accordion,collapsible,scroll-area - 反馈:
alert,alert-dialog,dialog,toast,sonner,progress - 导航:
dropdown-menu,menubar,navigation-menu,context-menu - 数据展示:
table,avatar,badge,hover-card,tooltip,popover - 其他:
calendar,command,carousel,resizable,sidebar
详见 src/components/ui/ 目录下的具体组件实现。
Next.js 使用文件系统路由,在 src/app/ 目录下创建文件夹即可添加路由:
# 创建新路由 /about
src/app/about/page.tsx
# 创建动态路由 /posts/[id]
src/app/posts/[id]/page.tsx
# 创建路由组(不影响 URL)
src/app/(marketing)/about/page.tsx
# 创建 API 路由
src/app/api/users/route.ts页面组件示例
// src/app/about/page.tsx
import { Button } from '@/components/ui/button';
export const metadata = {
title: '关于我们',
description: '关于页面描述',
};
export default function AboutPage() {
return (
<div>
<h1>关于我们</h1>
<Button>了解更多</Button>
</div>
);
}动态路由示例
// src/app/posts/[id]/page.tsx
export default async function PostPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
return <div>文章 ID: {id}</div>;
}API 路由示例
// src/app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json({ users: [] });
}
export async function POST(request: Request) {
const body = await request.json();
return NextResponse.json({ success: true });
}必须使用 pnpm 管理依赖
# ✅ 安装依赖
pnpm install
# ✅ 添加新依赖
pnpm add package-name
# ✅ 添加开发依赖
pnpm add -D package-name
# ❌ 禁止使用 npm 或 yarn
# npm install # 错误!
# yarn add # 错误!项目已配置 preinstall 脚本,使用其他包管理器会报错。
使用 Tailwind CSS v4
本项目使用 Tailwind CSS v4 进行样式开发,并已配置 shadcn 主题变量。
// 使用 Tailwind 类名
<div className="flex items-center gap-4 p-4 rounded-lg bg-background">
<Button className="bg-primary text-primary-foreground">
主要按钮
</Button>
</div>
// 使用 cn() 工具函数合并类名
import { cn } from '@/lib/utils';
<div className={cn(
"base-class",
condition && "conditional-class",
className
)}>
内容
</div>主题变量
主题变量定义在 src/app/globals.css 中,支持亮色/暗色模式:
--background,--foreground--primary,--primary-foreground--secondary,--secondary-foreground--muted,--muted-foreground--accent,--accent-foreground--destructive,--destructive-foreground--border,--input,--ring
推荐使用 react-hook-form + zod 进行表单开发:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
const formSchema = z.object({
username: z.string().min(2, '用户名至少 2 个字符'),
email: z.string().email('请输入有效的邮箱'),
});
export default function MyForm() {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: { username: '', email: '' },
});
const onSubmit = (data: z.infer<typeof formSchema>) => {
console.log(data);
};
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
<Input {...form.register('username')} />
<Input {...form.register('email')} />
<Button type="submit">提交</Button>
</form>
);
}服务端组件(推荐)
// src/app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store', // 或 'force-cache'
});
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<div>
{posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}客户端组件
'use client';
import { useEffect, useState } from 'react';
export default function ClientComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData);
}, []);
return <div>{JSON.stringify(data)}</div>;
}- 在
src/app/下创建文件夹和page.tsx - 使用 shadcn 组件构建 UI
- 根据需要添加
layout.tsx和loading.tsx
- 在
src/components/下创建组件文件(非 UI 组件) - 优先组合使用
src/components/ui/中的基础组件 - 使用 TypeScript 定义 Props 类型
推荐使用 React Context 或 Zustand:
// src/lib/store.ts
import { create } from 'zustand';
interface Store {
count: number;
increment: () => void;
}
export const useStore = create<Store>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));推荐使用 Prisma 或 Drizzle ORM,在 src/lib/db.ts 中配置。
- 框架: Next.js 16.1.1 (App Router)
- UI 组件: shadcn/ui (基于 Radix UI)
- 样式: Tailwind CSS v4
- 表单: React Hook Form + Zod
- 图标: Lucide React
- 字体: Geist Sans & Geist Mono
- 包管理器: pnpm 9+
- TypeScript: 5.x
- 必须使用 pnpm 作为包管理器
- 优先使用 shadcn/ui 组件 而不是从零开发基础组件
- 遵循 Next.js App Router 规范,正确区分服务端/客户端组件
- 使用 TypeScript 进行类型安全开发
- 使用
@/路径别名 导入模块(已配置)