第 25 章:部署与进阶
学习目标
- 掌握 Vercel 部署 Next.js
- 学会 Docker 部署
- 性能优化(图片、字体、代码分割)
- React 18 并发特性实战
一、Vercel 部署(Next.js 首选)
bash
npm install -g vercel
vercel login
vercel # 部署预览环境
vercel --prod # 部署生产环境或 GitHub 集成:
- 推代码到 GitHub
- vercel.com 选 "New Project"
- 选仓库,自动构建
Vercel 自动配置:
- Next.js 构建
- 全球 CDN
- 自动 HTTPS
- 预览环境(每个 PR)
二、Docker 部署
Dockerfile:
dockerfile
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]next.config.js:
js
module.exports = {
output: 'standalone',
};构建运行:
bash
docker build -t my-app .
docker run -p 3000:3000 my-app三、环境变量
bash
# .env.local(不提交)
DATABASE_URL=postgres://...
NEXT_PUBLIC_API_URL=https://api.example.comNEXT_PUBLIC_*前缀的会暴露到客户端- 其他只在服务端可用
tsx
// 服务端
process.env.DATABASE_URL
// 客户端
process.env.NEXT_PUBLIC_API_URL⚠️ 坑 1:不带
NEXT_PUBLIC_前缀的变量不会暴露到客户端,引用会是undefined。
四、性能优化
1. 图片优化
tsx
import Image from 'next/image';
<Image src="/hero.jpg" width={1200} height={600} priority alt="hero" />priority 给首屏大图,自动:
- WebP/AVIF 转换
- 懒加载
- 防止 CLS
2. 字体优化
tsx
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });无 FOIT(字体闪烁),自动子集化。
3. 代码分割
tsx
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('./Chart'), {
ssr: false,
loading: () => <Skeleton />,
});4. Bundle 分析
bash
npm install @next/bundle-analyzerjs
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' });
module.exports = withBundleAnalyzer({});bash
ANALYZE=true npm run build五、React 18 并发特性实战
1. useTransition(标记非紧急更新)
tsx
'use client';
import { useState, useTransition } from 'react';
function Search() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<Result[]>([]);
const [isPending, startTransition] = useTransition();
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setQuery(e.target.value); // ⚠️ 紧急:立即更新
startTransition(() => { // ✅ 非紧急:可中断
setResults(filterLargeList(e.target.value));
});
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<Results data={results} />
</>
);
}2. useDeferredValue(延迟值)
tsx
function Search() {
const [query, setQuery] = useState('');
const deferred = useDeferredValue(query); // �️ 延迟一帧
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ExpensiveResults query={deferred} /> {/* 用 deferred 做重计算 */}
</>
);
}3. Suspense for Data Fetching
tsx
import { Suspense } from 'react';
function App() {
return (
<Suspense fallback={<Skeleton />}>
<UserProfile />
</Suspense>
);
}
async function UserProfile() {
const user = await fetch('/api/user').then(r => r.json());
return <div>{user.name}</div>;
}4. 自动批处理(React 18+)
tsx
function Comp() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
function handle() {
setA(1);
setB(2);
// ✅ React 18:只触发一次渲染
}
}六、错误监控
bash
npm install @sentry/nextjs
npx @sentry/wizard@latest initts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
});七、SEO 与 metadata
tsx
// app/page.tsx
export const metadata = {
title: '我的网站',
description: 'Next.js 14 全栈教程',
openGraph: {
title: '我的网站',
images: ['/og.png'],
},
};sitemap / robots
ts
// app/sitemap.ts
export default async function sitemap() {
return [
{ url: 'https://example.com', lastModified: new Date() },
{ url: 'https://example.com/about', lastModified: new Date() },
];
}八、缓存策略
| 资源 | 策略 |
|---|---|
| HTML | 默认静态 / ISR |
| 图片 | 永久(内容哈希) |
| JS/CSS | 永久(内容哈希) |
| API 响应 | revalidate / no-store |
九、监控与告警
- Vercel Analytics(免费,Web Vitals)
- Sentry(错误监控)
- Google Analytics(用户行为)
- LogRocket(录屏回放)
十、React 19 预告(2025)
React 19 新特性:
- React Compiler(自动优化 re-render)
use()API(Promise/Context 直接读)ref作为 prop(无需 forwardRef)- Server Actions 稳定
<form>action 自动 reset
⚠️ 坑 2:升级前看 react.dev/blog,部分破坏性变更(如
refprop)。
十一、学习路径建议
入门:HTML/CSS/JS → React 基础 → Hooks
进阶:TypeScript → Next.js → 性能优化
生态:Zustand/React Router → Tailwind/shadcn
工程:Vitest/Playwright → CI/CD → 监控
架构:Micro Frontends → Monorepo(Turborepo)十二、本章小结
| 要点 | 关键 |
|---|---|
| 部署 | Vercel(零配置)/ Docker 自托管 |
| 环境变量 | NEXT_PUBLIC_* 暴露客户端 |
| 性能 | next/image、动态 import、bundle analyzer |
| 并发 | useTransition / useDeferredValue / Suspense |
| 监控 | Sentry + Vercel Analytics |
| React 19 | Compiler、use() API、ref prop |
动手练习
- Vercel 部署:把 Next.js 项目部署到 Vercel,看预览环境
- Bundle 分析:跑
ANALYZE=true,找出最大依赖,尝试拆动态加载 - useTransition:用 transition 包裹大列表过滤,感受输入不卡顿
- 错误监控:接入 Sentry,故意 throw 看是否上报
文档完结,React 18 + Next.js 14 全栈学习完成。