1. Next.js 是什么?

1.1 传统 React SPA 的问题

传统的 React 应用是单页应用(SPA),它的工作流程:

  1. 浏览器下载一个几乎为空的 HTML 文件
  2. 下载 JavaScript 文件
  3. JavaScript 执行,渲染页面内容

这带来几个问题:

  • 首屏加载慢:用户会先看到白屏,然后才看到内容
  • SEO 不友好:搜索引擎爬虫抓取到的是空 HTML,无法索引页面内容
  • 社交分享无预览:分享链接时,无法生成包含标题、描述、图片的卡片

React 本身只解决了“交互”问题,但没有解决“内容如何快速到达用户”的问题。

1.2 Next.js 的特性

Next.js 是 React 的全栈框架,它在 React 之上增加了:

1)服务端渲染(SSR):在服务器生成完整 HTML,直接返回给浏览器

2)静态站点生成(SSG):构建时生成 HTML,速度最快

3)增量静态再生(ISR):定时重新生成静态页面,兼顾速度和实时性

4)文件系统路由:根据 app 目录下的文件自动生成路由

5)API 路由:在同一个项目中编写后端接口

6)图片优化、字体优化:内置最佳实践

简单说:Next.js 让 React 应用可以直接运行在服务器上

1.3 CSR vs SSR vs SSG vs ISR

渲染模式渲染时机优点缺点
CSR(客户端渲染)浏览器交互丰富,适合后台首屏慢,SEO 差
SSR(服务端渲染)每次请求SEO 好,首屏快服务器压力大
SSG(静态生成)构建时速度最快,可放 CDN内容更新需重新构建
ISR(增量静态再生)构建时 + 定时刷新兼顾速度和实时性复杂度稍高

Next.js 默认使用 SSG + SSR 混合模式,你可以在每个页面选择最适合的渲染方式。

1.4 App Router

创建项目与项目结构

创建 Next.js 项目:

npx create-next-app@latest my-app --typescript --tailwind --app

App Router 目录结构

my-app/
├── app/
│   ├── layout.tsx          # 根布局(所有页面共享)
│   ├── page.tsx            # 首页(/)
│   ├── about/
│   │   └── page.tsx        # /about 页面
│   ├── blog/
│   │   ├── [slug]/
│   │   │   └── page.tsx    # 动态路由 /blog/xxx
│   │   └── layout.tsx      # 博客区域布局(可选)
│   └── api/
│       └── tasks/
│           └── route.ts    # API 路由 /api/tasks
├── public/                 # 静态资源
├── styles/
│   └── globals.css         # 全局样式
└── next.config.js          # Next.js 配置文件

关键约定

  • page.tsx:定义页面组件,导出即可访问路由
  • layout.tsx:包裹子页面的共享布局(如导航栏)
  • [param]:动态路由参数,params 对象会传入页面
  • route.ts:定义 API 端点(GET、POST、PUT、DELETE)

2. 路由系统 App Router

2.1 文件即路由.js

文件即路由: page.js / layout.js

page.tsx:每个目录下的 page.tsx 对应一个 URL 路径。

文件路径对应 URL
app/page.tsx/
app/about/page.tsx/about
app/blog/page.tsx/blog

layout.tsx:同一目录下的共享布局,会包裹所有子页面。

// app/blog/layout.tsx
export default function BlogLayout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <header>博客导航</header>
      <main>{children}</main>
    </div>
  )
}

根布局(app/layout.tsx) 必须存在,包含 <html> 和 <body>

2.2 动态路由

动态路由:[slug] / [...catchAll]

单段动态路由:app/blog/[slug]/page.tsx

// URL: /blog/hello-world
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>文章:{params.slug}</h1>
}

可选全匹配[[...slug]],如果路径为空,params.slug 为 undefined

2.3 路由组 与平行路由

路由组 (group) 、平行路由 @folder

路由组:用括号 (folder) 组织代码,不影响 URL。

app/
├── (marketing)/
│   ├── about/page.tsx      → /about
│   └── contact/page.tsx    → /contact
└── (admin)/
    └── dashboard/page.tsx  → /dashboard

平行路由:用 @folder 命名,在同一页面同时渲染多个独立区域,解决布局复用问题。

app/
├── @analytics/
│   └── page.tsx            # 平行路由插槽
├── @sidebar/
│   └── page.tsx
└── page.tsx

平行路由的典型场景:同时显示统计图表和侧边栏,且它们可以独立加载。

2.4 导航

导航: Link、useRouter

<Link> 组件:客户端页面切换,无刷新。

import Link from 'next/link'

export default function Nav() {
  return (
    <nav>
      <Link href="/">首页</Link>
      <Link href="/about">关于</Link>
    </nav>
  )
}

useRouter 钩子:编程式导航

'use client'
import { useRouter } from 'next/navigation'

export default function LoginButton() {
  const router = useRouter()
  return <button onClick={() => router.push('/dashboard')}>登录</button>
}

2.5 路由拦截与中间

路由拦截与中间件:Middleware

中间件在请求到达页面之前执行,常用于认证、日志、重定向

创建 middleware.ts 放在项目根目录:

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')?.value
  const { pathname } = request.nextUrl

  // 未登录用户访问 /dashboard 时重定向到 /login
  if (!token && pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

// 配置中间件匹配哪些路径
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*']
}

中间件运行环境:Edge Runtime(轻量级),不能使用 Node.js 核心模块。

3. 渲染模式

3.1 服务端组件原理

在 Next.js 中,默认所有组件都是服务端组件(Server Components)。

服务端组件的特点:

1)在服务器上运行,生成 HTML 字符串发给浏览器

2)不能使用 useStateuseEffect、浏览器 API(如 localStorage

3)不能绑定事件(如 onClick

4)可以直接 async/await 获取数据

5)代码不会被打包到客户端 JavaScript 中,体积更小

// app/user/page.tsx(默认就是服务端组件)
async function getUser(id: number) {
  const res = await fetch(`https://api.example.com/user/${id}`)
  return res.json()
}

export default async function UserPage() {
  const user = await getUser(1)
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  )
}

为什么用服务端组件?

1)数据获取在服务器完成,浏览器无需等待 JS 加载

2)敏感逻辑(如 API token)不会暴露给客户端

3)减少客户端 JS 体积

3.2 客户端组件 与 'use client'

客户端组件 Client Components 、与 'use client'

需要交互、事件、状态的地方,必须标记为客户端组件

在文件顶部写上 'use client'

'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => setCount(count + 1)}>
      点击次数:{count}
    </button>
  )
}

可以同时使用服务端和客户端组件:父组件是服务端,子组件是客户端,数据通过 props 传递。

// app/page.tsx(服务端组件)
import ClientCounter from './ClientCounter'

export default async function Page() {
  const initialCount = await getInitialCount()
  return <ClientCounter initialCount={initialCount} />
}
// app/ClientCounter.tsx
'use client'

export default function ClientCounter({ initialCount }: { initialCount: number }) {
  const [count, setCount] = useState(initialCount)
  // ...
}

客户端组件的最佳实践

  • 尽量将客户端组件放在叶子节点(靠近 UI 层)
  • 不要将整个页面变成客户端组件
  • 通过 props 从服务端组件传递初始数据

3.3 静态渲染与动态渲染

静态渲染:Static Generation

动态渲染:Dynamic

静态渲染:在构建时生成 HTML,每次请求直接返回缓存内容。

// app/blog/[slug]/page.tsx
// 默认是静态渲染,前提是不使用动态函数(cookies、headers、searchParams)
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)  // 获取数据
  return <div>{post.content}</div>
}

动态渲染:每次请求时在服务器上实时生成页面。

触发动态渲染的方式:

  • 使用 cookies()headers() 函数
  • 使用 searchParams prop(查询参数)
  • 在 fetch 中设置 cache: 'no-store'
  • 在路由段配置 export const dynamic = 'force-dynamic'
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic'  // 强制动态渲染

export default async function Dashboard() {
  const data = await fetch('https://api.example.com/dashboard', { cache: 'no-store' })
  const dashboardData = await data.json()
  return <div>...</div>
}

3.4 流式渲染与 Suspense

流式渲染:Streaming

流式渲染:将页面拆分成多个块,服务器一边生成一边发送给浏览器,用户不用等整个页面完成。

使用 <Suspense> 包裹异步组件:

// app/page.tsx
import { Suspense } from 'react'
import SlowComponent from './SlowComponent'
import FastComponent from './FastComponent'

export default function Page() {
  return (
    <div>
      <FastComponent />    {/* 无需包裹,很快 */}
      <Suspense fallback={<div>加载中...</div>}>
        <SlowComponent />  {/* 这个慢,会先显示 fallback,加载后替换 */}
      </Suspense>
    </div>
  )
}

流式渲染的效果

  • 用户不用看白屏,先看到部分内容
  • 每个 Suspense 边界独立加载
  • 非常适合优化首屏体验

3.5 渲染模式的选择策略

页面类型推荐模式原因
营销页、博客文章静态渲染(SSG)内容不常变,CDN 缓存,速度最快
产品列表(实时价格)动态渲染(SSR)+ ISR需要实时性,但可缓存短时间
用户仪表盘(个性化)动态渲染(SSR)每个用户数据不同,无法缓存
后台管理页客户端渲染(CSR)首屏不关键,交互复杂

组合使用:一个页面可以混合不同渲染模式的组件。

  • 导航栏、页脚 → 静态(服务端组件)
  • 用户头像 → 动态(服务端组件,根据 cookie 显示)
  • 交互按钮 → 客户端组件

4. 数据获取

4.1 服务端数据获取

服务端数据获取:async component

服务端组件可以直接使用 async/await 获取数据。

// app/users/page.tsx
async function getUsers() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users')
  if (!res.ok) throw new Error('Failed to fetch users')
  return res.json()
}

export default async function UsersPage() {
  const users = await getUsers()
  return (
    <ul>
      {users.map((user: any) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

并行数据获取:使用 Promise.all 减少请求时间。

export default async function Dashboard() {
  const [user, posts] = await Promise.all([
    fetchUser(),
    fetchPosts()
  ])
  return (
    <div>
      <UserInfo user={user} />
      <PostList posts={posts} />
    </div>
  )
}

4.2 客户端数据获

客户端组件中,推荐使用 SWR 或 TanStack Query 来处理数据获取和缓存。

安装 SWR:

npm install swr
'use client'
import useSWR from 'swr'

const fetcher = (url: string) => fetch(url).then(res => res.json())

export default function ClientPosts() {
  const { data, error, isLoading } = useSWR('/api/posts', fetcher)

  if (isLoading) return <div>加载中...</div>
  if (error) return <div>加载失败</div>
  return <ul>{data.map((post: any) => <li key={post.id}>{post.title}</li>)}</ul>
}

什么时候用客户端数据获取?

  • 需要实时轮询数据(如股票价格)
  • 数据变化频繁且只影响局部组件
  • 不想通过 API 路由中转,直接调用第三方服务

4.3 缓存与重新验证

缓存与重新验证:fetch 的 cache 和 next.revalidate

Next.js 扩展了原生 fetch,增加了缓存控制选项。

默认静态缓存

fetch('https://api.example.com/posts')  // 等同于 { cache: 'force-cache' }

禁用缓存(每次实时获取)

fetch('https://api.example.com/posts', { cache: 'no-store' })

按时间重新验证(ISR 的核心)

// app/blog/[slug]/page.tsx
export default async function BlogPage({ params }: { params: { slug: string } }) {
  const post = await fetch(`https://api.example.com/posts/${params.slug}`, {
    next: { revalidate: 60 }  // 每 60 秒重新验证一次
  }).then(res => res.json())
  return <div>{post.content}</div>
}

针对整个路由段设置缓存时间

// app/blog/layout.tsx
export const revalidate = 60  // 该目录下所有页面 60 秒后重新生成

4.4 数据获取模式与最佳实践

模式1:服务端获取 + 静态生成(SSG)

适用于:博客文章、产品介绍页

优点:最快,可缓存到 CDN

配置:不设 cache: 'no-store',也不使用动态函数

模式2:服务端获取 + 增量静态再生(ISR)

适用于:新闻列表、商品目录

优点:兼顾实时性和性能

配置:fetch 中设置 next: { revalidate: 60 } 或路由段 export const revalidate = 60

模式3:客户端获取 + 缓存库

适用于:仪表盘、后台管理

优点:交互丰富,可配合轮询、乐观更新

配置:SWR 或 TanStack Query

最佳实践

  • 默认优先使用服务端组件 + 服务端获取
  • 只在需要交互、实时数据时使用客户端获取
  • 用 Suspense + 流式渲染优化用户体验
  • 避免在服务端组件中直接使用浏览器 API(localStorage、window)

5. API 路由 Route Handlers

5.1 创建 API 路由

创建 API 路由:route.js

在 Next.js 中,API 路由定义在 app/api/ 目录下,通过 route.ts 或 route.js 文件导出 HTTP 方法处理函数。

基本结构

app/
└── api/
    └── tasks/
        └── route.ts      # 处理 /api/tasks 请求
// app/api/tasks/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json({ message: '获取任务列表' })
}

export async function POST(request: Request) {
  const body = await request.json()
  return NextResponse.json({ message: '创建任务', data: body }, { status: 201 })
}

路由文件命名:必须叫 route.ts(或 route.js),不能随意命名。

5.2 请求与响应处理

1. 获取请求数据

import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
  // 获取 JSON body
  const body = await request.json()
  const { title, completed } = body

  // 获取查询参数
  const searchParams = request.nextUrl.searchParams
  const page = searchParams.get('page') || '1'

  // 获取请求头
  const authHeader = request.headers.get('authorization')

  return NextResponse.json({ title, page })
}

2. 响应方法

// 返回 JSON
return NextResponse.json({ id: 1, name: '张三' })

// 自定义状态码
return NextResponse.json({ error: '未找到' }, { status: 404 })

// 返回文本
return new NextResponse('Hello World', {
  status: 200,
  headers: { 'Content-Type': 'text/plain' }
})

// 重定向
return NextResponse.redirect(new URL('/login', request.url))

5.3 动态 API 路由与参数

动态 API 路由通过文件夹命名 [param] 捕获 URL 参数。

app/
└── api/
    └── tasks/
        └── [id]/
            └── route.ts    # 处理 /api/tasks/123
// app/api/tasks/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'

interface RouteParams {
  params: { id: string }
}

export async function GET(
  request: NextRequest,
  { params }: RouteParams
) {
  const id = parseInt(params.id)
  // 根据 id 获取数据...
  return NextResponse.json({ id, title: '任务标题' })
}

export async function PUT(
  request: NextRequest,
  { params }: RouteParams
) {
  const id = parseInt(params.id)
  const body = await request.json()
  // 更新数据...
  return NextResponse.json({ id, ...body })
}

export async function DELETE(
  request: NextRequest,
  { params }: RouteParams
) {
  const id = parseInt(params.id)
  // 删除数据...
  return NextResponse.json({ message: '删除成功' })
}

通配符路由:[...slug]/route.ts 捕获多段参数。

app/api/files/[...slug]/route.ts
// 请求 /api/files/images/logo.png → params.slug = ['images', 'logo.png']

5.4 中间件与 API 权限

中间件:Middleware

中间件可用于全局的 API 权限校验、日志记录、请求改写。

// middleware.ts(项目根目录)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 只对 API 路由进行认证
  if (request.nextUrl.pathname.startsWith('/api/')) {
    const token = request.headers.get('authorization')
    if (!token || token !== 'Bearer secret-token') {
      return NextResponse.json({ error: '未授权' }, { status: 401 })
    }
  }
  return NextResponse.next()
}

export const config = {
  matcher: '/api/:path*'
}

在 API 路由中单独校验

// app/api/posts/route.ts
import { NextResponse } from 'next/server'
import { verifyToken } from '@/lib/auth'

export async function POST(request: Request) {
  const authHeader = request.headers.get('authorization')
  const token = authHeader?.split(' ')[1]

  if (!token || !verifyToken(token)) {
    return NextResponse.json({ error: '未授权' }, { status: 401 })
  }

  // 继续处理...
}

6. 优化与配置

6.1 图片优化

图片优化:next/image

next/image 组件自动优化图片:懒加载、响应式尺寸、WebP 格式转换、防止布局偏移。

import Image from 'next/image'

export default function Avatar() {
  return (
    <Image
      src="/avatar.png"
      alt="用户头像"
      width={100}
      height={100}
      priority  // 首屏图片优先加载
    />
  )
}

远程图片:需在 next.config.js 中配置允许的域名。

// next.config.js
module.exports = {
  images: {
    domains: ['images.unsplash.com', 'cdn.example.com'],
  },
}

使用远程图片:

<Image
  src="https://images.unsplash.com/photo-123"
  width={800}
  height={600}
  alt="风景"
/>

6.2 字体优化

字体优化:next/font

next/font 自动优化字体,避免布局偏移(CLS)和额外的网络请求。

// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })
const robotoMono = Roboto_Mono({ subsets: ['latin'] })

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

局部使用:

import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })

<h1 className={inter.className}>标题</h1>

自定义字体文件:

import localFont from 'next/font/local'

const myFont = localFont({
  src: './fonts/MyFont.woff2',
  display: 'swap',
})

6.3 脚本优化

脚本优化:next/script

next/script 控制第三方脚本的加载时机,优化性能。

import Script from 'next/script'

export default function Layout() {
  return (
    <>
      <Script
        src="https://www.googletagmanager.com/gtag/js"
        strategy="afterInteractive"  // 页面可交互后加载
      />
      <Script
        src="https://example.com/analytics.js"
        strategy="lazyOnload"       // 空闲时加载
        onLoad={() => console.log('加载完成')}
      />
    </>
  )
}

strategy 选项

  • beforeInteractive:在页面可交互前加载(用于关键脚本)
  • afterInteractive:页面可交互后立即加载(默认)
  • lazyOnload:浏览器空闲时加载
  • worker:在 Web Worker 中加载(实验性)

6.4 元数据与 SEO

元数据与 SEO:metadata API

Next.js 提供了 Metadata API 来管理页面标题、描述、Open Graph 等。

静态元数据

// app/about/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: '关于我们',
  description: '了解我们的故事和团队',
  openGraph: {
    title: '关于我们',
    description: '了解我们的故事和团队',
    images: 'https://example.com/og-image.jpg',
  },
}

export default function AboutPage() {
  return <div>关于页面</div>
}

动态元数据(根据数据生成):

// app/blog/[slug]/page.tsx
import { Metadata } from 'next'

async function getPost(slug: string) {
  const res = await fetch(`https://api.example.com/posts/${slug}`)
  return res.json()
}

export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: post.coverImage,
    },
  }
}

6.5 环境变量与配置文件

环境变量.env.local.env.development.env.production

# .env.local
API_BASE_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=G-XXXXXXXX
  • 以 NEXT_PUBLIC_ 开头的变量会暴露给客户端
  • 其他变量只能在服务端使用

使用方式:

// 服务端
const apiUrl = process.env.API_BASE_URL

// 客户端
const gaId = process.env.NEXT_PUBLIC_GA_ID

next.config.js 常用配置:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  // 允许跨域图片
  images: {
    domains: ['images.unsplash.com'],
  },
  // 重定向
  async redirects() {
    return [
      {
        source: '/old-blog/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
    ]
  },
  // 环境变量(不推荐,建议用 .env 文件)
  env: {
    CUSTOM_KEY: 'value',
  },
}

module.exports = nextConfig

6.6 打包与部署

打包与部署:Vercel / 自托管

构建

npm run build   # 生成 .next 目录
npm start       # 生产模式运行

部署到 Vercel(推荐):

  1. 将代码推送到 GitHub
  2. 登录 Vercel,导入仓库
  3. Vercel 自动检测 Next.js 并部署
  4. 每次 git push 自动重新部署

环境变量在 Vercel 上配置:项目设置 → Environment Variables。

自托管(Node.js 服务器)

# 构建
npm run build

# 运行
npm start

需要安装 Node.js 并确保环境变量正确。

自托管(Docker)

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["npm", "start"]

静态导出(可选)

如果项目完全静态(无服务端逻辑),可以配置 output: 'export' 导出纯静态文件,部署到任何静态托管服务。

// next.config.js
module.exports = {
  output: 'export',
}

7. 附录

7.1 Pages Router 与 App Router 对比

Next.js 提供了两种路由方式:传统的 Pages Routerpages/ 目录)和现代的 App Routerapp/ 目录)。以下是它们的主要区别:

特性Pages RouterApp Router
引入版本Next.js 1.xNext.js 13+
路由定义文件名直接映射(pages/about.js → /about文件夹 + page.jsapp/about/page.js → /about
布局机制需要手动包裹组件,无嵌套布局内置 layout.js,支持嵌套布局
数据获取getServerSidePropsgetStaticPropsgetStaticPathsasync 组件 + fetch 扩展
渲染模式SSR, SSG, ISRSSR, SSG, ISR, 流式渲染
服务端组件不支持默认(可减少客户端 JS)
客户端组件所有组件都是客户端组件需标记 'use client'
API 路由pages/api/*.jsapp/api/*/route.js
中间件_middleware.js 或 middleware.js 在 pages 同级middleware.ts 在根目录,更灵活
元数据/SEO需要 next/head 或第三方库内置 metadata API
图片优化next/imagenext/image(相同)
动态路由[slug].js[...slug].js[slug]/page.js[...slug]/page.js
路由组X (group) 用于组织代码
平行路由X @folder 同时渲染多个页面
拦截路由X(.)(..)(..)(..) 拦截同层级或上层路由
学习曲线平缓较陡(新概念较多)
生产稳定性成熟稳定已稳定,推荐新项目使用

迁移建议

新项目直接使用 App Router

旧项目可以逐步迁移,App Router 可以和 Pages Router 共存

7.2 常用命令速查表

命令说明
npx create-next-app@latest创建新项目
npm run dev启动开发服务器(默认 http://localhost:3000
npm run build生产环境构建
npm start运行生产服务器(需要先 build)
npm run lint运行 ESLint 检查
next info打印当前环境信息(用于问题排查)
next build --debug构建时输出更多调试信息
next export导出静态站点(需配置 output: 'export'
next telemetry启用/禁用匿名使用数据收集

常用 npm 脚本(package.json

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }
}

7.3 推荐学习资源

1. 官方文档

Next.js 官方文档(英文)

Next.js 中文文档(官方支持中文)

Next.js App Router 迁移指南

2. 视频教程

Next.js 官方 YouTube 频道

Vercel 官方视频

Lee Robinson 的 Next.js 教程

3. 实战学习

Next.js Learn 教程(免费) —— 官方互动教程

Vercel 模板市场 —— 各种开箱即用的 Next.js 项目模板

4. 社区与博客

Next.js GitHub 仓库

Next.js 博客

Vercel 博客

Awesome Next.js —— 精选资源列表

5. 工具与插件

Vercel CLI —— 部署到 Vercel

ESLint 插件 eslint-plugin-next

Bundle Analyzer —— 分析打包体积

Next.js 完全指南到此结束!🎉

更多推荐