Next.js
Next.js 是一个基于 React 的全栈开发框架,提供了服务端渲染、静态生成、API 路由等强大功能。
核心特性
1. 路由系统
Next.js 14 引入了全新的 App Router,基于 React Server Components:
// app/page.tsx - 页面组件
export default function HomePage() {
return <h1>Welcome to Next.js</h1>;
}
// app/blog/[slug]/page.tsx - 动态路由
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>Post: {params.slug}</h1>;
}
// app/layout.tsx - 布局组件
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>{children}</body>
</html>
);
}
2. 数据获取
// 服务端组件中获取数据
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // 增量静态再生成
});
return res.json();
}
export default async function Page() {
const data = await getData();
return <main>{/* 使用数据 */}</main>;
}
// 客户端数据获取
'use client';
import { useQuery } from '@tanstack/react-query';
export default function ClientComponent() {
const { data } = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(res => res.json()),
});
return <div>{/* 使用数据 */}</div>;
}
3. 服务器组件
// 服务器组件(默认)
export default async function ServerComponent() {
const data = await getData(); // 直接在服务器端获取数据
return <div>{data.title}</div>;
}
// 客户端组件
'use client';
export default function ClientComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
4. API 路由
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await getUsers();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json();
const newUser = await createUser(data);
return NextResponse.json(newUser, { status: 201 });
}
Pages Router(传统版本)
1. 基础路由
// pages/index.tsx - 首页
export default function Home() {
return <h1>Welcome to Next.js</h1>;
}
// pages/posts/[id].tsx - 动态路由
export default function Post({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
// 获取动态路由参数
export async function getStaticPaths() {
const posts = await getPosts();
return {
paths: posts.map((post) => ({
params: { id: post.id },
})),
fallback: false,
};
}
export async function getStaticProps({ params }) {
const post = await getPost(params.id);
return {
props: {
post,
},
};
}
2. 数据获取方法
// 1. getStaticProps - 静态生成
export async function getStaticProps() {
const data = await fetch('https://api.example.com/data');
const posts = await data.json();
return {
props: {
posts,
},
revalidate: 60, // ISR: 60秒后重新生成
};
}
// 2. getServerSideProps - 服务端渲染
export async function getServerSideProps(context) {
const { req, res } = context;
const data = await fetch('https://api.example.com/data');
const posts = await data.json();
return {
props: {
posts,
},
};
}
// 3. 客户端数据获取
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetcher);
if (error) return <div>Failed to load</div>;
if (!data) return <div>Loading...</div>;
return <div>Hello {data.name}!</div>;
}
3. API 路由
// pages/api/posts.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
const posts = await getPosts();
res.status(200).json(posts);
} else if (req.method === 'POST') {
const post = await createPost(req.body);
res.status(201).json(post);
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
4. 自定义 App 和 Document
// pages/_app.tsx
import type { AppProps } from 'next/app';
import Layout from '../components/Layout';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head>
<link rel="stylesheet" href="..." />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
5. 路由导航
// 1. Link 组件
import Link from 'next/link';
export default function Navigation() {
return (
<nav>
<Link
href="/posts/[id]"
as={`/posts/${post.id}`}
>
{post.title}
</Link>
</nav>
);
}
// 2. 编程式导航
import { useRouter } from 'next/router';
export default function Navigation() {
const router = useRouter();
return (
<button onClick={() => router.push('/posts/1')}>
Go to Post
</button>
);
}
6. 样式处理
// 1. CSS Modules
// styles/Home.module.css
.container {
padding: 2rem;
}
// pages/index.tsx
import styles from '../styles/Home.module.css';
export default function Home() {
return (
<div className={styles.container}>
<h1>Welcome</h1>
</div>
);
}
// 2. Styled JSX
export default function Button() {
return (
<div>
<button>Click me</button>
<style jsx>{`
button {
background: blue;
color: white;
}
`}</style>
</div>
);
}
7. 图片优化
import Image from 'next/image';
export default function Avatar() {
return (
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={300}
layout="responsive"
/>
);
}
8. 国际化
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'fr', 'zh'],
defaultLocale: 'en',
},
};
// pages/index.tsx
import { useRouter } from 'next/router';
export default function IndexPage() {
const router = useRouter();
const { locale } = router;
return (
<div>
<h1>{locale === 'en' ? 'Welcome' : '欢迎'}</h1>
<button onClick={() => router.push('/', '/', { locale: 'zh' })}>
切换到中文
</button>
</div>
);
}
9. 环境变量
# .env.local
DB_HOST=localhost
DB_USER=myuser
NEXT_PUBLIC_ANALYTICS_ID=abcdef
// 使用环境变量
// 服务端
const dbHost = process.env.DB_HOST;
// 客户端
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;
10. 错误处理
// pages/404.js - 自定义404页面
export default function Custom404() {
return <h1>404 - Page Not Found</h1>;
}
// pages/500.js - 自定义500页面
export default function Custom500() {
return <h1>500 - Server-side error occurred</h1>;
}
// pages/_error.js - 自定义错误页面
function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
);
}