开发环境搭建与工程脚手架
工欲善其事,必先利其器。本章从零开始,搭建一个生产级的 Next.js 16 开发环境——从 Node.js 版本管理、create-next-app 脚手架、pnpm 包管理、ESLint + Prettier 代码规范,到环境变量的类型安全管理,每一步都给出最佳实践和踩坑提示。
1. 基础环境准备
1.1 Node.js 版本管理
Next.js 16 要求 Node.js 18.18+,推荐使用 Node.js 20 LTS 或 22 LTS。使用 fnm(Fast Node Manager)管理多版本:
# macOS / Linux 安装 fnm
curl -fsSL https://fnm.vercel.app/install | bash
# Windows(通过 winget)
winget install Schniz.fnm
# 安装并使用 Node.js 20 LTS
fnm install 20
fnm use 20
fnm default 20
# 验证
node -v # v20.x.x
npm -v # 10.x.x在项目根目录创建 .node-version 文件,确保团队成员使用相同版本:
echo "20" > .node-version1.2 包管理器:pnpm
Next.js 16 支持 npm、yarn、pnpm。推荐 pnpm——速度快、磁盘空间节省、严格的依赖管理:
# 通过 corepack 启用(Node.js 内置)
corepack enable
corepack prepare pnpm@latest --activate
# 验证
pnpm -v # 9.x.x在 package.json 中锁定包管理器:
{
"packageManager": "[email protected]",
"engines": {
"node": ">=20.0.0"
}
}1.3 编辑器配置
VS Code / Cursor 推荐扩展
// .vscode/extensions.json
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"prisma.prisma",
"ms-vscode.vscode-typescript-next",
"formulahendry.auto-rename-tag"
]
}编辑器通用配置
// .vscode/settings.json
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"typescript.tsdk": "node_modules/typescript/lib",
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}2. 使用 create-next-app 创建项目
2.1 一键创建
pnpm create next-app@latest ai-saas-platform
# 交互式选项(推荐选择)
✔ Would you like to use TypeScript? → Yes
✔ Would you like to use ESLint? → Yes
✔ Would you like to use Tailwind CSS? → Yes
✔ Would you like your code inside a `src/` directory? → No
✔ Would you like to use App Router? → Yes
✔ Would you like to use Turbopack for next dev? → Yes (Next.js 16 默认)
✔ Would you like to customize the import alias? → Yes → @/*选择 No。在 Next.js App Router 中,app/ 目录已经是很好的组织边界。加 src/ 多一层嵌套,收益不大,反而增加路径深度。如果团队有强烈偏好,选 Yes 也没问题——只是路径变成 src/app/。
2.2 生成的目录结构
ai-saas-platform/
├── app/
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx # 根布局
│ └── page.tsx # 首页
├── public/
│ ├── file.svg
│ ├── globe.svg
│ └── ...
├── .eslintrc.json # ESLint 配置
├── .gitignore
├── next-env.d.ts # Next.js 类型声明
├── next.config.ts # Next.js 配置
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs # PostCSS(Tailwind)
├── tailwind.config.ts # Tailwind 配置
└── tsconfig.json # TypeScript 配置
2.3 验证项目启动
cd ai-saas-platform
pnpm dev
# 输出
▲ Next.js 16.x.x
- Local: http://localhost:3000
- Network: http://192.168.x.x:3000
- Turbopack: enabled
✓ Starting...
✓ Ready in 1.2s访问 http://localhost:3000,看到 Next.js 默认欢迎页即成功。
3. 项目结构规划
3.1 AI SaaS 项目推荐目录结构
脚手架生成的结构太简单,我们需要扩展为生产级结构:
ai-saas-platform/
├── app/ # 路由与页面
│ ├── (auth)/ # 认证相关路由组
│ │ ├── login/page.tsx
│ │ ├── register/page.tsx
│ │ └── layout.tsx
│ ├── (dashboard)/ # 仪表盘路由组
│ │ ├── chat/
│ │ ├── knowledge/
│ │ ├── settings/
│ │ └── layout.tsx
│ ├── (marketing)/ # 营销页路由组
│ │ ├── page.tsx # 首页
│ │ ├── pricing/page.tsx
│ │ └── layout.tsx
│ ├── admin/ # 管理后台
│ ├── api/ # API Routes
│ │ ├── chat/route.ts
│ │ ├── webhook/
│ │ └── trpc/[trpc]/route.ts
│ ├── layout.tsx # 根布局
│ ├── not-found.tsx # 404
│ └── globals.css
├── components/ # 共享组件
│ ├── ui/ # shadcn/ui 组件
│ ├── chat/ # 业务组件:聊天
│ ├── knowledge/ # 业务组件:知识库
│ └── layout/ # 布局组件
├── lib/ # 工具库
│ ├── db/ # 数据库
│ │ ├── schema.ts # Drizzle/Prisma Schema
│ │ ├── client.ts # 数据库客户端
│ │ └── migrations/
│ ├── ai/ # AI 相关
│ │ ├── models.ts # 模型配置
│ │ └── tools.ts # Agent 工具
│ ├── auth/ # 认证
│ │ └── config.ts # Auth.js 配置
│ ├── stripe/ # 支付
│ │ └── config.ts
│ ├── email/ # 邮件
│ │ └── templates/
│ ├── utils.ts # 通用工具函数
│ └── constants.ts # 常量
├── hooks/ # 自定义 React Hooks
├── types/ # 全局类型定义
├── config/ # 应用配置
│ └── site.ts # 站点元数据
├── public/ # 静态资源
├── .env.local # 本地环境变量
├── .env.example # 环境变量模板
├── next.config.ts
├── tailwind.config.ts
├── tsconfig.json
└── package.json
3.2 路由分组策略
Next.js 的 Route Groups(圆括号目录)是组织大型应用的关键——它们影响布局但不影响 URL:
app/
├── (marketing)/ # 营销页 — 公开布局(Header + Footer)
│ ├── layout.tsx # 公开布局
│ ├── page.tsx # / (首页)
│ ├── pricing/page.tsx # /pricing
│ └── blog/page.tsx # /blog
├── (auth)/ # 认证页 — 居中卡片布局
│ ├── layout.tsx # 认证布局
│ ├── login/page.tsx # /login
│ └── register/page.tsx # /register
├── (dashboard)/ # 仪表盘 — 侧边栏布局(需登录)
│ ├── layout.tsx # 仪表盘布局(含侧边栏)
│ ├── chat/page.tsx # /chat
│ └── settings/page.tsx # /settings
└── admin/ # 管理后台 — 独立布局(需管理员权限)
├── layout.tsx
└── page.tsx # /admin
4. ESLint + Prettier 代码规范
4.1 ESLint Flat Config
Next.js 16 推荐使用 ESLint 9 的 Flat Config 格式:
# 安装依赖
pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-react eslint-plugin-react-hooks eslint-config-next// eslint.config.mjs
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { FlatCompat } from '@eslint/eslintrc'
import js from '@eslint/js'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
})
export default [
js.configs.recommended,
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
rules: {
'@typescript-eslint/no-unused-vars': ['warn', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
}],
'react/no-unescaped-entities': 'off',
'@next/next/no-img-element': 'off',
},
},
]4.2 Prettier 配置
pnpm add -D prettier prettier-plugin-tailwindcss// .prettierrc
{
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"plugins": ["prettier-plugin-tailwindcss"]
}prettier-plugin-tailwindcss 会自动排序 Tailwind 类名,保持团队一致性。
// .prettierignore
node_modules
.next
pnpm-lock.yaml
4.3 Git Hooks:Husky + lint-staged
确保每次提交都通过代码检查:
pnpm add -D husky lint-staged
pnpm exec husky init// package.json 中添加
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,css}": ["prettier --write"]
}
}# .husky/pre-commit
pnpm exec lint-staged5. 环境变量管理
5.1 Next.js 环境变量规则
Next.js 有一套明确的环境变量加载规则:
| 文件 | 加载时机 | Git 跟踪 |
|---|---|---|
.env | 所有环境 | ✅ 是 |
.env.local | 所有环境(覆盖 .env) | ❌ 否 |
.env.development | next dev | ✅ 是 |
.env.production | next build / next start | ✅ 是 |
关键规则:只有以 NEXT_PUBLIC_ 前缀的变量才会暴露给客户端浏览器。
# .env.local
# 🔒 服务端专用(不会暴露给浏览器)
DATABASE_URL="postgresql://user:pass@localhost:5432/ai_saas"
OPENAI_API_KEY="sk-..."
STRIPE_SECRET_KEY="sk_test_..."
AUTH_SECRET="your-auth-secret"
# 🌐 客户端可用(会暴露给浏览器)
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_POSTHOG_KEY="phc_..."5.2 类型安全的环境变量
裸用 process.env.XXX 没有类型提示,也无法在构建时校验。使用 @t3-oss/env-nextjs 实现类型安全:
pnpm add @t3-oss/env-nextjs zod// lib/env.ts
import { createEnv } from '@t3-oss/env-nextjs'
import { z } from 'zod'
export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
OPENAI_API_KEY: z.string().startsWith('sk-'),
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
AUTH_SECRET: z.string().min(32),
RESEND_API_KEY: z.string(),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
AUTH_SECRET: process.env.AUTH_SECRET,
RESEND_API_KEY: process.env.RESEND_API_KEY,
NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
},
})使用时获得完整类型提示:
import { env } from '@/lib/env'
// ✅ 类型安全
env.DATABASE_URL // string(保证是有效 URL)
env.OPENAI_API_KEY // string(保证以 sk- 开头)
// ❌ 编译报错:client 环境变量不能在服务端反过来用
// env.NEXT_PUBLIC_APP_URL // 在客户端组件中可用如果缺少必需的环境变量,应用在构建时就会报错,而不是运行时才发现。
5.3 环境变量模板
# .env.example(提交到 Git,作为团队参考)
# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/ai_saas"
# Auth
AUTH_SECRET="generate-with-openssl-rand-base64-32"
# AI
OPENAI_API_KEY="sk-..."
# Stripe
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
# Email
RESEND_API_KEY="re_..."
# Public
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_POSTHOG_KEY=""6. next.config.ts 基础配置
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
// React Compiler(实验性,推荐开启)
experimental: {
reactCompiler: true,
},
// 图片域名白名单
images: {
remotePatterns: [
{ hostname: 'avatars.githubusercontent.com' },
{ hostname: '*.amazonaws.com' },
{ hostname: '*.cloudflare.com' },
],
},
// 独立部署模式(Docker 部署需要)
output: 'standalone',
// 重定向
async redirects() {
return [
{
source: '/app',
destination: '/chat',
permanent: true,
},
]
},
}
export default nextConfig7. 第一次提交
# 初始化 Git
git init
git add .
git commit -m "feat: initial Next.js 16 project setup
- Next.js 16 with App Router and Turbopack
- TypeScript strict mode
- Tailwind CSS v4 + PostCSS
- ESLint Flat Config + Prettier
- Husky + lint-staged
- Type-safe environment variables (@t3-oss/env-nextjs)
- Production-ready directory structure"本章小结
- 基础环境:Node.js 20+ (fnm 管理) + pnpm 9+ (corepack 启用) + VS Code/Cursor 配置
- create-next-app:使用 App Router + TypeScript + Tailwind CSS + Turbopack 创建项目
- 目录结构:按 Route Groups 组织(marketing/auth/dashboard/admin),按功能组织 lib 目录
- 代码规范:ESLint Flat Config + Prettier + Tailwind 类名排序 + Husky pre-commit
- 环境变量:
@t3-oss/env-nextjs+ Zod 实现构建时校验、运行时类型安全 - next.config.ts:React Compiler、图片白名单、standalone 输出
下一章,我们将深入 App Router 的目录结构,理解每一个特殊文件的作用和最佳实践。