Next.js 入门指南:从零开始构建现代 Web 应用 Next.js 是一个基于 React 的全栈开发框架,由 Vercel 开发和维护。它提供了一系列强大的功能,帮助开发者构建高性能、可扩展的 Web 应用。本文将带你深入了解 Next.js,从基础概念到实践应用。
目录
Next.js 简介
环境准备
核心概念
项目实战
高级特性
部署与优化
最佳实践
Next.js 简介 为什么选择 Next.js?
开箱即用 :
零配置路由系统
内置开发服务器和构建工具
自动代码分割和打包优化
内置 CSS 和 Sass 支持
性能优化 :
自动图片优化和响应式支持
字体加载优化
代码分割和预加载
智能构建优化
灵活部署 :
支持静态导出(Static Export)
支持服务端渲染(SSR)
Vercel 平台一键部署
支持自定义服务器
开发体验 :
快速刷新(Fast Refresh)
TypeScript 原生支持
路由预加载
开发时错误提示
核心特性
服务端渲染(SSR)
SEO 友好:搜索引擎可以抓取完整的页面内容
更快的首屏加载:用户无需等待 JavaScript 加载完成
更好的社交媒体分享:预渲染的 meta 标签
支持动态数据:每次请求都可以获取最新数据
静态站点生成(SSG)
构建时预渲染所有页面
可以部署到任何静态托管服务
极快的页面加载速度
降低服务器成本
增量静态再生成(ISR)
按需更新静态页面
无需重新部署整个站点
可以设置更新频率
保持数据的新鲜度
混合渲染
可以在同一应用中混合使用 SSG、SSR 和 ISR
根据页面需求选择最适合的渲染方式
优化用户体验和性能
灵活的数据获取策略
环境准备 系统要求
Node.js :
版本要求:14.6.0 或更高
推荐使用 LTS 版本
需要包含 npm 包管理器
开发环境 :
支持的操作系统:MacOS、Windows、Linux
推荐的代码编辑器:VS Code
推荐的浏览器:Chrome 或 Firefox
包管理器 :
npm(Node.js 自带)
yarn(可选)
pnpm(可选,更快的包管理器)
创建项目
使用 create-next-app :
1 2 3 4 5 6 7 8 npx create-next-app@latest my-next-app yarn create next-app my-next-app npx create-next-app@latest my-next-app --typescript
配置选项 :
1 2 3 4 5 6 √ Would you like to use TypeScript? ... No / Yes √ Would you like to use ESLint? ... No / Yes √ Would you like to use Tailwind CSS? ... No / Yes √ Would you like to use `src/` directory? ... No / Yes √ Would you like to use App Router? ... No / Yes √ Would you like to customize the default import alias ? ... No / Yes
项目结构 :
1 2 3 4 5 6 7 my-next-app/ ├── app/ # App Router 目录 ├── components/ # 可复用组件 ├── public/ # 静态资源 ├── styles/ # CSS 样式文件 ├── package.json # 项目配置 └── next.config.js # Next.js 配置
核心概念 1. 路由系统 Next.js 13+ 的 App Router 提供了基于文件系统的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 export default function RootLayout ({ children, }: { children: React.ReactNode } ) { return ( <html lang ="zh" > <body > {children}</body > </html > ) } export default function Home ( ) { return ( <main > <h1 > 欢迎来到 Next.js</h1 > </main > ) } export default function BlogPost ({ params, }: { params: { slug: string } } ) { return <article > 文章: {params.slug}</article > }
2. 数据获取 Next.js 提供了多种数据获取方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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 > {data.map(item => ( <div key ={item.id} > {item.title}</div > ))} </main > ) } 'use client' import { useState, useEffect } from 'react' export default function ClientComponent ( ) { const [data, setData] = useState (null ) useEffect (() => { fetch ('/api/data' ) .then (res => res.json ()) .then (setData) }, []) return data ? <div > {data.title}</div > : <div > Loading...</div > }
项目实战 让我们通过构建一个博客应用来实践 Next.js 的核心功能。
1. 项目初始化 1 2 npx create-next-app@latest blog-app --typescript --tailwind --app cd blog-app
2. 创建页面组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import { Metadata } from 'next' import './globals.css' export const metadata : Metadata = { title : '我的博客' , description : '使用 Next.js 构建的个人博客' , } export default function RootLayout ({ children, }: { children: React.ReactNode } ) { return ( <html lang ="zh" > <body > <nav className ="bg-gray-800 text-white p-4" > <div className ="container mx-auto" > <h1 className ="text-xl font-bold" > 我的博客</h1 > </div > </nav > <main className ="container mx-auto p-4" > {children} </main > </body > </html > ) }
3. 实现文章列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import { getPosts } from '@/lib/posts' import Link from 'next/link' export default async function Home ( ) { const posts = await getPosts () return ( <div className ="grid gap-4" > {posts.map(post => ( <article key ={post.id} className ="border p-4 rounded-lg" > <h2 className ="text-2xl font-bold" > <Link href ={ `/posts /${post.slug }`}> {post.title} </Link > </h2 > <p className ="text-gray-600 mt-2" > {post.excerpt}</p > </article > ))} </div > ) }
4. 添加 API 路由 1 2 3 4 5 6 7 8 import { NextResponse } from 'next/server' import { getPosts } from '@/lib/posts' export async function GET ( ) { const posts = await getPosts () return NextResponse .json (posts) }
5. 实现文章详情页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import { getPost } from '@/lib/posts' import { notFound } from 'next/navigation' export default async function PostPage ({ params, }: { params: { slug: string } } ) { const post = await getPost (params.slug ) if (!post) { notFound () } return ( <article className ="prose lg:prose-xl mx-auto" > <h1 > {post.title}</h1 > <div dangerouslySetInnerHTML ={{ __html: post.content }} /> </article > ) }
高级特性 1. 中间件使用 中间件可以在请求处理之前执行代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware (request : NextRequest ) { const token = request.cookies .get ('token' ) if (!token && request.nextUrl .pathname .startsWith ('/admin' )) { return NextResponse .redirect (new URL ('/login' , request.url )) } return NextResponse .next () } export const config = { matcher : '/admin/:path*' , }
2. 图片优化 Next.js 提供了内置的图片优化组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import Image from 'next/image' export default function Avatar ( ) { return ( <Image src ="/avatar.jpg" alt ="用户头像" width ={64} height ={64} className ="rounded-full" priority /> ) }
3. 国际化路由 支持多语言路由配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { match } from '@formatjs/intl-localematcher' import Negotiator from 'negotiator' let locales = ['en' , 'zh' ]let defaultLocale = 'zh' function getLocale (request : Request ): string { const negotiatorHeaders : Record <string , string > = {} request.headers .forEach ((value, key ) => (negotiatorHeaders[key] = value)) const languages = new Negotiator ({ headers : negotiatorHeaders }).languages () return match (languages, locales, defaultLocale) } export function middleware (request : Request ) { const pathname = request.nextUrl .pathname const pathnameIsMissingLocale = locales.every ( locale => !pathname.startsWith (`/${locale} /` ) && pathname !== `/${locale} ` ) if (pathnameIsMissingLocale) { const locale = getLocale (request) return Response .redirect ( new URL (`/${locale} ${pathname} ` , request.url ) ) } }
部署与优化 1. 构建优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const nextConfig = { images : { domains : ['example.com' ], }, experimental : { serverActions : true , }, webpack : (config, { dev, isServer } ) => { return config }, } module .exports = nextConfig
2. 性能优化
代码分割
1 2 3 4 5 6 import dynamic from 'next/dynamic' const DynamicComponent = dynamic (() => import ('../components/Heavy' ), { loading : () => <p > 加载中...</p > , ssr : false })
预加载页面
1 2 3 4 5 6 7 8 9 10 11 12 13 import { useEffect } from 'react' import { useRouter } from 'next/router' export default function Page ( ) { const router = useRouter () useEffect (() => { router.prefetch ('/about' ) }, [router]) return <div > 当前页面</div > }
3. Vercel 部署
推送代码到 GitHub
在 Vercel 中导入项目
配置环境变量
自动部署
最佳实践 1. 目录结构 1 2 3 4 5 6 7 ├── app/ │ ├── api/ # API 路由 │ ├── (auth)/ # 认证相关路由组 │ ├── components/ # 页面组件 │ └── lib/ # 工具函数 ├── public/ # 静态资源 └── types/ # TypeScript 类型定义
2. 代码规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Post = { id : string title : string content : string publishedAt : Date } import { getPosts } from '@/lib/posts' export default async function PostsPage ( ) { const posts = await getPosts () return <PostList posts ={posts} /> }
3. 错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 'use client' export default function Error ({ error, reset, }: { error: Error reset: () => void } ) { return ( <div className ="text-center py-10" > <h2 className ="text-2xl font-bold" > 出错了</h2 > <p className ="text-red-500" > {error.message}</p > <button onClick ={() => reset()} className="mt-4 px-4 py-2 bg-blue-500 text-white rounded" > 重试 </button > </div > ) }
学习资源
结语 Next.js 是一个强大而灵活的框架,通过本文的学习,你应该已经掌握了其基本概念和使用方法。继续深入学习和实践,你将能够构建出高质量的现代 Web 应用。
本文将持续更新,欢迎关注最新的 Next.js 开发动态。