Skip to content

Lesson 17:Next.js 15 项目搭建 — 进入全栈的大门 ​

🧩 本节信息卡(学习前先看) ​

  • 阶段定位:Phase 3(实战篇)
  • 推荐时长:90~150 分钟(首次学习)
  • 先修要求:完成 Phase 1~2,理解路由、状态管理与异步请求
  • 学习产出:创建 Next.js 15 项目,完成首页、商品列表和错误兜底
✅ 本节完成标准(自检清单)
  • [ ] 我可以独立复现文中的核心代码片段
  • [ ] 我能解释“为什么这样实现”,而不只是“照着写”
  • [ ] 我记录了至少 1 个踩坑点和修复方法

🧭 本节统一学习流程 ​

  1. 学习目标:先明确本节要解决的业务问题与核心 API。
  2. 主线实战:跟随课程实现可运行功能(先跑通,再优化)。
  3. 原理深挖:理解为什么这样设计,以及常见误区。
  4. 练习挑战:完成 L1/L2(阶段收官课建议加 L3)巩固迁移能力。
  5. 本节小结:回顾“做了什么 / 学到了什么 / 下节前检查项”。

建议节奏:阅读 20% + 编码 60% + 复盘 20%。

🎯 本节目标:理解 Next.js 的定位和四种渲染模式,搭建全栈电商项目骨架。

📦 本节产出:一个基于 Next.js 15 App Router 的全栈项目,含首页和商品列表页面。

一、为什么需要 Next.js? ​

在 Phase 2,我们用 Vite + React Router 搭建了一个 纯客户端渲染 (CSR) 的 SPA。 纯 CSR 在内容展示、首屏加载和服务端能力上有一些取舍:

痛点CSR (Vite)SSR (Next.js)
SEO初始 HTML 通常没有页面内容,索引依赖爬虫执行 JS预渲染 HTML 包含内容,更便于抓取
首屏速度内容展示依赖 JS 下载和执行可先显示 HTML;实际速度仍受数据查询、网络和缓存影响
后端 API需接入已有 API、后端服务或 BaaSApp Router 内置 Route Handlers,可与页面放在同一项目
数据库前端不能直接访问数据库Server Components 可直接查数据库!

二、四种渲染模式速览 ​

同一个 Next.js 项目里,不同路由可以采用不同的渲染和缓存策略。下面四类是入门分类;实际页面也能组合服务端内容与客户端交互。

模式渲染时机优点缺点
CSR浏览器端适合依赖浏览器状态的交互初始内容依赖 JS,需评估索引与加载体验
SSR每次请求时可按请求生成 HTML有服务端计算成本;数据是否新鲜还取决于数据缓存
SSG构建时(npm run build)速度极快、可 CDN 缓存数据不会自动更新
ISR预生成后按时间或事件重新验证复用静态结果并更新内容时间到期通常由后续请求触发更新,可能短暂看到旧内容

三、初始化项目 ​

bash
npx create-next-app@15 phase3-ecommerce --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd phase3-ecommerce
npm pkg set overrides.next.postcss=8.5.23
npm install

NOTE

我们选择了 --app 来启用 App Router(而非旧版的 Pages Router)。 App Router 是 Next.js 13+ 的架构革新,也是 React Server Components 的官方落地载体。

本阶段使用 Node.js 24 LTS、Next.js 15.5 维护版本和 React 19,请提交 package-lock.json。@15 防止脚手架跨到 Next.js 16;截至本次校对,15 系列最新维护版本为 15.5.25。交互提示可选择默认 Turbopack 开发服务器。安装后用 npm ls next react react-dom 核对依赖,后续遵循 Next.js 支持政策 安装同系列安全更新。安装选项见 Next.js 15 安装文档。

Next.js 15.5.25 的传递依赖包含旧 PostCSS;上面的定向 override 使用已修复版本 8.5.23,本次已验证与课程项目构建兼容。原因见 PostCSS 安全公告。安装新的 Next.js 15 维护版后,可重新检查 npm audit 和构建结果,确认是否仍需此覆盖。

3.1 项目结构 ​

phase3-ecommerce/
├── src/
│   └── app/                    ← App Router 的核心目录
│       ├── layout.tsx          ← 根布局(等同 Phase 2 的 RootLayout)
│       ├── page.tsx            ← 首页 (/)
│       ├── globals.css         ← 全局样式
│       └── products/
│           └── page.tsx        ← 商品列表 (/products)
├── public/                     ← 静态资源
├── next.config.ts              ← Next.js 配置
├── postcss.config.mjs          ← Tailwind 4 的 PostCSS 配置
└── package.json

保留脚手架的 Tailwind 4 配置,globals.css 中应有 @import "tailwindcss";。Tailwind 4 默认使用 CSS 配置,不会生成 tailwind.config.ts。

3.2 核心区别:文件即路由 ​

在 Phase 2 里,路由需要手动在 main.tsx 中写 createBrowserRouter([...]) 配置路由数组。

Next.js App Router 使用文件系统路由——文件夹结构就是 URL 路径!

规则很简单:

  • 每个文件夹代表一段 URL
  • page.tsx 是该路径的页面组件
  • layout.tsx 是该路径及其子路径的共享布局
  • [id] 方括号 = 动态路由参数(等同 React Router 中的 :id)

四、编写首页和商品列表 ​

4.1 根布局 app/layout.tsx ​

tsx
// src/app/layout.tsx
import type { Metadata } from 'next'
import Link from 'next/link'
import './globals.css'

export const metadata: Metadata = {
  title: 'ShopNext — 全栈电商',
  description: '用 Next.js 15 构建的全栈电商平台',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN">
      <body className="min-h-screen bg-gray-50 text-gray-900 antialiased">
        <header className="bg-white border-b border-gray-200 shadow-sm">
          <nav className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between">
            <Link href="/" className="text-xl font-bold text-indigo-600">🛒 ShopNext</Link>
            <div className="flex gap-6 text-sm font-medium text-gray-600">
              <Link href="/products" className="hover:text-indigo-600 transition-colors">商品</Link>
              {/* 登录和购物车路由在后续课程创建后再添加导航链接。 */}
            </div>
          </nav>
        </header>
        <main>{children}</main>
      </body>
    </html>
  )
}

IMPORTANT

站内导航使用 Next.js 的 <Link>,支持客户端导航和预取;外部站点链接仍可使用 <a>。

4.2 首页 app/page.tsx ​

tsx
// src/app/page.tsx
import Link from 'next/link'

export default function Home() {
  return (
    <div className="max-w-7xl mx-auto px-4 py-20 text-center">
      <h1 className="text-5xl font-extrabold bg-linear-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
        欢迎来到 ShopNext
      </h1>
      <p className="mt-6 text-xl text-gray-500 max-w-2xl mx-auto">
        一个用 Next.js 15 + React Server Components 构建的全栈电商平台。
      </p>
      <Link
        href="/products"
        className="mt-8 inline-block bg-indigo-600 text-white px-8 py-3 rounded-xl font-semibold hover:bg-indigo-700 transition-colors"
      >
        浏览商品 →
      </Link>
    </div>
  )
}

4.3 商品列表假数据 app/products/page.tsx ​

tsx
// src/app/products/page.tsx

// 模拟数据(后面会替换成 Prisma 数据库查询)
const products = [
  { id: '1', name: 'React 19 实战手册', price: 99, image: '📘' },
  { id: '2', name: 'TypeScript 进阶指南', price: 129, image: '📗' },
  { id: '3', name: 'Next.js 全栈开发', price: 159, image: '📕' },
]

export default function ProductsPage() {
  return (
    <div className="max-w-7xl mx-auto px-4 py-12">
      <h1 className="text-3xl font-bold mb-8">全部商品</h1>
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
        {products.map(product => (
          <article
            key={product.id}
            className="group bg-white rounded-2xl border border-gray-200 overflow-hidden hover:shadow-lg transition-shadow"
          >
            <div className="h-48 bg-gray-100 flex items-center justify-center text-6xl">
              {product.image}
            </div>
            <div className="p-5">
              <h2 className="font-semibold text-lg group-hover:text-indigo-600 transition-colors">
                {product.name}
              </h2>
              <p className="mt-2 text-2xl font-bold text-indigo-600">¥{product.price}</p>
            </div>
          </article>
        ))}
      </div>
    </div>
  )
}

启动项目:

bash
npm run dev

访问 http://localhost:3000,你会看到一个有渐变标题的首页。点击"浏览商品"就能看到三本书的卡片。

右键→查看页面源代码:商品列表的初始 HTML 已包含商品内容。这说明页面经过了服务端预渲染,但不代表每次请求都执行 SSR:本节的静态数据页面在生产构建时可以预渲染为静态页面。Phase 2 的纯 CSR 配置需要 JS 执行后才生成内容;Vite 本身也能用于 SSR。商品详情页将在 L18 实现,L22 再补搜索和 SEO,本节卡片先只展示内容。


五、🧠 深度专题:Next.js 请求生命周期 ​

Hydration(水合) 是 React 在浏览器中接管预渲染 HTML、关联客户端组件逻辑和事件处理器的过程,SSR 和静态预渲染都可能用到。HTML 本身仍有链接、表单等原生能力;Server Components 不在浏览器中水合,需交互的 Client Components 才参与水合。参见 服务端与客户端组件。


六、约定式文件:error.tsx 与 not-found.tsx ​

App Router 有一套约定式特殊文件,自动处理加载、错误和 404 等状态:

src/app/products/
├── page.tsx           ← 页面组件
├── loading.tsx        ← 加载状态(见 L27)
├── error.tsx          ← 运行时错误兜底(必须是 Client Component!)
└── not-found.tsx      ← 404 页面

6.1 error.tsx — 路由级 ErrorBoundary ​

还记得 Phase 2 的 L16 中我们手写了 ErrorBoundary 类组件吗?Next.js 把这个能力内置到了文件约定中:

tsx
// src/app/products/error.tsx
'use client'  // error.tsx 必须是 Client Component!

export default function ProductsError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div className="max-w-lg mx-auto px-4 py-20 text-center">
      <p className="text-6xl mb-4">😵</p>
      <h2 className="text-xl font-bold mb-2">商品页面出错了</h2>
      <p className="text-gray-500 mb-6 text-sm">{error.message}</p>
      <button onClick={reset}
        className="bg-indigo-600 text-white px-6 py-2 rounded-xl hover:bg-indigo-700">
        🔄 重试
      </button>
    </div>
  )
}

reset() 会尝试重新渲染错误边界内的内容;它不会自动保证服务端数据已重新获取,持续的错误仍会显示兜底。它也不能捕获同一层布局自身的错误,详见 error.tsx 约定。

6.2 global-error.tsx — 根级兜底 ​

根布局 (app/layout.tsx) 的错误不会被 app/error.tsx 捕获(因为 error.tsx 被嵌套在 layout.tsx 内部)。需要 global-error.tsx:

tsx
// src/app/global-error.tsx
'use client'

export default function GlobalError({
  reset
}: {
  error: Error; reset: () => void
}) {
  return (
    <html lang="zh-CN">
      <body className="flex items-center justify-center min-h-screen">
        <div className="text-center">
          <h1 className="text-2xl font-bold mb-4">系统错误</h1>
          <button onClick={reset} className="bg-indigo-600 text-white px-6 py-2 rounded-xl">
            重试
          </button>
        </div>
      </body>
    </html>
  )
}

NOTE

global-error.tsx 必须自己渲染 <html> 和 <body> 标签,因为它替换了整个根布局。


七、练习 ​

  1. 创建 app/about/page.tsx,写一个"关于我们"页面,注意观察 URL 是否自动映射。
  2. 打开浏览器的"查看源代码",对比 Phase 2 Vite 项目的 HTML 和 Next.js 的 HTML,理解 SSR 的核心价值。

📌 本节小结 ​

你做了什么你学到了什么
初始化了 Next.js 15 全栈电商项目Next.js 提供预渲染、路由和服务端能力
创建了首页和商品列表页App Router 文件系统路由
查看了预渲染返回的页面 HTMLCSR / SSR / SSG / ISR 四种渲染模式
—Hydration 水合的概念

项目驱动 · 边写边学 · React 19