先看框架承担哪部分职责
Next.js 完整框架:把路由、渲染、取数、缓存和部署拆成五个决定。先确定代码执行位置,再决定数据新鲜度,最后用测量选择优化。
最小练习与预期结果:选一个商品详情页,标出服务器读取、客户端交互与失效入口;刷新和直接访问都应得到符合权限与新鲜度约定的内容。
具体 API 与安装版本以模块基线和本篇官方来源为准。先完成这条数据路径,再展开后面的高级配置;框架名称变化后,输入边界、状态归属和失败处理仍是需要理解的机制。
架构取舍
- 对一个页面说明哪些代码在服务端执行、哪些需要浏览器交互,以及这样划分的证据
- 用嵌套路由、布局、错误和加载边界表达真实的页面状态
- 在服务端组件与客户端组件之间选择数据、状态和事件的归属,避免把秘密或大量依赖带到客户端
- 将性能与部署目标量化为可测指标和可回退的发布步骤
高级开发能力
- 根据流量、数据一致性和团队边界评估扩展方案,而不把“可扩展”当成框架默认属性
- 用高级路由和布局解决具体导航、并行视图或拦截交互,并给出直接访问时的预期结果
- 为每类数据定义缓存键、失效触发点、陈旧窗口和写后读取行为
- 用锁定版本、构建日志、性能测量和发布回退组成可复现的开发工作流
概述
Next.js 提供路由、渲染和构建工具,但缓存一致性、数据库事务、授权、外部服务故障和运行成本仍由应用设计承担。本章以这些工具的职责边界为中心:先把一个页面的输入、读取、缓存和错误结果做对,再比较并行路由、流式渲染或构建优化是否解决了可测的瓶颈。
Next.js 16 核心变化速览
从 Next.js 15 升级到 16 时,以下项目会影响日常开发。发布版本、稳定性状态和迁移命令变化较快;执行升级前,以 Next.js 官方升级指南 与项目锁定版本为准,并在独立分支完成构建和关键路径验收。
1. Turbopack 成为默认打包器
项目升级后先确认开发与生产构建实际使用的打包器,再决定是否迁移配置。不要把宣传中的构建速度比例当作本项目结果:在同一硬件、锁文件和缓存条件下记录 next dev 启动、一次热更新和 next build 的耗时与产物大小。是否仍可使用 Webpack 回退、命令名称和 turbopack 配置位置,均以锁定版本的官方升级指南为准。
2. Async Request APIs 完全异步
params、searchParams、cookies()、headers()、draftMode() 均为 Promise/异步 API。Next.js 15 的同步兼容层在 16 中已完全移除,同步访问会直接报错:
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ q?: string }>;
}) {
const { slug } = await params;
const { q } = await searchParams;
// ...
}
3. 缓存语义:默认更少缓存
- 缓存默认值和配置项可能随版本升级改变。对每个读取路径写明它是请求时读取、预渲染还是显式缓存,并用两次读取与一次写入后的读取验证实际行为。
- 需要标签失效或写后读取时,使用当前版本文档规定的缓存 API 和参数;为失效失败、并发写入和权限不同的读取补测试。
- 不要只因为名称从 PPR 或其他实验标志迁移就假定响应更快;先测量真实页面的首字节、可交互时间和陈旧数据窗口。
4. Instant Navigations 与路由增强
客户端导航和预取策略会影响请求数、内存与陈旧数据。用浏览器网络面板和真实路由跳转记录“是否预取、何时失效、直接访问是否等价”,不要把“接近即时”当作未测量页面的结论。
5. middleware.ts 弃用,更名为 proxy.ts
proxy.ts 明确网络边界,运行于 Node.js runtime;导出函数更名为 proxy。Edge runtime 场景可暂留 middleware.ts(已弃用,未来移除)。
6. 其他 breaking/移除项
- AMP 支持移除(
useAmp、config.amp等 API 全部删除) next lint命令移除,next build不再运行 lint,直接使用 ESLint/Biome CLIserverRuntimeConfig/publicRuntimeConfig移除,改用环境变量next/image默认值变化:minimumCacheTTL提升至 4 小时、qualities收敛为[75]、imageSizes移除 16、本地带查询串的 src 需配置images.localPatterns- 最低运行时要求 Node.js 20.9+
Next.js 16 架构深度解析
核心架构原理
// src/types/nextjs-architecture.ts
export interface NextJSArchitecture {
// 渲染架构
rendering: {
serverComponents: '服务器组件 - 零JavaScript客户端渲染'
clientComponents: '客户端组件 - 交互性和状态管理'
hybridRendering: '混合渲染 - 结合服务端和客户端优势'
streaming: '流式渲染 - 渐进式页面加载'
}
// 路由架构
routing: {
appRouter: 'App Router - 基于文件系统的现代路由'
nestedLayouts: '嵌套布局 - 复杂页面结构支持'
parallelRoutes: '并行路由 - 多个独立布局区域'
interceptingRoutes: '拦截路由 - 模态框和覆盖层'
}
// 缓存架构
caching: {
dataCache: '数据缓存 - 自动数据获取缓存'
fullRouteCache: '全路由缓存 - 静态页面缓存'
incrementalCache: '增量缓存 - ISR和重新验证'
clientCache: '客户端缓存 - 浏览器缓存策略'
}
// 构建架构
build: {
turbopack: 'Turbopack - 下一代构建工具'
codeSplitting: '代码分割 - 自动和手动分割'
bundleAnalysis: '包分析 - 构建优化分析'
assetOptimization: '资源优化 - 图片、字体、CSS优化'
}
}
App Router 深度原理
// src/lib/app-router-core.ts
import { cache } from 'react'
// 路由段配置接口
export interface RouteSegmentConfig {
// 动态配置
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
revalidate?: number | false
runtime?: 'nodejs' | 'edge'
// 缓存配置
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store'
preferredRegion?: string
// 元数据配置
maxDuration?: number
}
// 路由构建器
export class RouteBuilder {
private static routeCache = new Map<string, any>()
// 创建动态路由配置
static createRouteConfig(config: RouteSegmentConfig) {
return {
// 动态渲染模式
dynamic: config.dynamic || 'auto',
// 重新验证策略
revalidate: config.revalidate || false,
// 运行时环境
runtime: config.runtime || 'nodejs',
// 缓存策略
fetchCache: config.fetchCache || 'auto',
// 区域优化
preferredRegion: config.preferredRegion || 'auto',
// 最大执行时间
maxDuration: config.maxDuration || 30
}
}
// 缓存路由结果
static cacheRoute<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
if (this.routeCache.has(key)) {
return this.routeCache.get(key)
}
const result = fetcher()
this.routeCache.set(key, result)
return result
}
// 清除路由缓存
static clearRouteCache(key?: string) {
if (key) {
this.routeCache.delete(key)
} else {
this.routeCache.clear()
}
}
}
// 使用示例
export const dynamicRouteConfig = RouteBuilder.createRouteConfig({
dynamic: 'force-dynamic',
revalidate: 60, // 60秒重新验证
runtime: 'nodejs',
fetchCache: 'auto',
maxDuration: 30
})
Server Components 高级模式
// src/components/advanced-server-components.tsx
import { cache } from 'react'
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
// 缓存数据获取函数
const getCachedData = cache(async (id: string) => {
const response = await fetch(`https://api.example.com/data/${id}`, {
next: { revalidate: 3600, tags: [`data-${id}`] }
})
return response.json()
})
// 高级服务器组件
interface AdvancedServerComponentProps {
id: string
fallback?: React.ReactNode
errorFallback?: React.ComponentType<{ error: Error; reset: () => void }>
}
export async function AdvancedServerComponent({
id,
fallback = <div>Loading...</div>,
errorFallback
}: AdvancedServerComponentProps) {
// 并行数据获取
const [data, relatedData, metadata] = await Promise.all([
getCachedData(id),
getRelatedData(id),
getMetadata(id)
])
// 条件渲染
if (!data) {
return <div>Data not found</div>
}
return (
<ErrorBoundary FallbackComponent={errorFallback || DefaultErrorFallback}>
<Suspense fallback={fallback}>
<ComponentContent data={data} relatedData={relatedData} metadata={metadata} />
</Suspense>
</ErrorBoundary>
)
}
// 组件内容
function ComponentContent({ data, relatedData, metadata }: any) {
return (
<div className="space-y-6">
<header>
<h1 className="text-4xl font-bold">{data.title}</h1>
<p className="text-gray-600">{metadata.description}</p>
</header>
<main>
<div dangerouslySetInnerHTML={{ __html: data.content }} />
</main>
<aside>
<RelatedContent data={relatedData} />
</aside>
</div>
)
}
// 默认错误回退
function DefaultErrorFallback({ error, reset }: { error: Error; reset: () => void }) {
return (
<div className="p-4 bg-red-50 border border-red-200 rounded-lg">
<h3 className="text-red-800 font-medium">组件加载失败</h3>
<p className="text-red-600 text-sm mt-1">{error.message}</p>
<button
onClick={reset}
className="mt-2 px-3 py-1 bg-red-600 text-white text-sm rounded hover:bg-red-700"
>
重试
</button>
</div>
)
}
// 获取相关数据
async function getRelatedData(id: string) {
const response = await fetch(`https://api.example.com/related/${id}`)
return response.json()
}
// 获取元数据
async function getMetadata(id: string) {
const response = await fetch(`https://api.example.com/metadata/${id}`)
return response.json()
}
// 相关内容组件
function RelatedContent({ data }: { data: any[] }) {
return (
<div className="space-y-4">
<h3 className="text-xl font-semibold">相关内容</h3>
{data.map((item) => (
<div key={item.id} className="p-4 border rounded-lg">
<h4 className="font-medium">{item.title}</h4>
<p className="text-sm text-gray-600 mt-1">{item.excerpt}</p>
</div>
))}
</div>
)
}
高级路由模式
并行路由和槽位系统
// src/app/dashboard/@analytics/page.tsx
export default function AnalyticsPage() {
return (
<div className="space-y-6">
<h2 className="text-2xl font-bold">数据分析仪表板</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<MetricCard
title="总用户数"
value="1,234"
change="+12%"
trend="up"
/>
<MetricCard
title="活跃用户"
value="856"
change="+5%"
trend="up"
/>
<MetricCard
title="新增用户"
value="124"
change="-2%"
trend="down"
/>
<MetricCard
title="留存率"
value="78%"
change="+3%"
trend="up"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<ChartContainer title="用户增长趋势" />
<ChartContainer title="活跃度分析" />
</div>
</div>
)
}
// src/app/dashboard/@settings/page.tsx
export default function SettingsPage() {
return (
<div className="space-y-6">
<h2 className="text-2xl font-bold">系统设置</h2>
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">基本设置</h3>
<SettingsForm />
</div>
</div>
)
}
// src/app/dashboard/layout.tsx
import { Slot } from '@radix-ui/react-slot'
export default function DashboardLayout({
children,
analytics,
settings
}: {
children: React.ReactNode
analytics?: React.ReactNode
settings?: React.ReactNode
}) {
return (
<div className="flex h-screen bg-gray-100">
{/* 侧边栏 */}
<aside className="w-64 bg-white shadow-md">
<DashboardSidebar />
</aside>
{/* 主内容区 */}
<main className="flex-1 overflow-auto">
<div className="p-6">
{children}
</div>
{/* 并行槽位 */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 p-6">
<div className="lg:col-span-2 space-y-6">
{analytics}
</div>
<div className="space-y-6">
{settings}
</div>
</div>
</main>
</div>
)
}
拦截路由和模态框系统
// src/app/photos/[id]/page.tsx
import { notFound } from 'next/navigation'
import { getPhotoById } from '@/lib/photos'
interface PhotoPageProps {
// Next.js 16:params 为 Promise
params: Promise<{ id: string }>
}
export default async function PhotoPage({ params }: PhotoPageProps) {
const { id } = await params
const photo = await getPhotoById(id)
if (!photo) {
notFound()
}
return (
<div className="container mx-auto py-8">
<div className="max-w-4xl mx-auto">
<img
src={photo.url}
alt={photo.title}
className="w-full h-auto rounded-lg shadow-lg"
/>
<h1 className="text-3xl font-bold mt-6">{photo.title}</h1>
<p className="text-gray-600 mt-2">{photo.description}</p>
</div>
</div>
)
}
// src/app/@modal/(.)photos/[id]/page.tsx
export default async function PhotoModal({ params }: PhotoPageProps) {
const { id } = await params
const photo = await getPhotoById(id)
if (!photo) {
return null
}
return <PhotoModalWrapper photo={photo} />
}
// src/components/PhotoModalWrapper.tsx
'use client'
import { useRouter, usePathname } from 'next/navigation'
import { createPortal } from 'react-dom'
import { useEffect } from 'react'
interface PhotoModalWrapperProps {
photo: {
id: string
title: string
url: string
description: string
}
}
export function PhotoModalWrapper({ photo }: PhotoModalWrapperProps) {
const router = useRouter()
const pathname = usePathname()
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
router.back()
}
}
document.addEventListener('keydown', handleEscape)
return () => document.removeEventListener('keydown', handleEscape)
}, [router])
const handleBackdropClick = (e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
router.back()
}
}
// 创建Portal到body
if (typeof document === 'undefined') {
return null
}
return createPortal(
<div
className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-4"
onClick={handleBackdropClick}
>
<div className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-auto">
<div className="relative">
<img
src={photo.url}
alt={photo.title}
className="w-full h-auto"
/>
<button
onClick={() => router.back()}
className="absolute top-4 right-4 bg-white rounded-full p-2 shadow-lg hover:bg-gray-100"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="p-6">
<h2 className="text-2xl font-bold">{photo.title}</h2>
<p className="text-gray-600 mt-2">{photo.description}</p>
</div>
</div>
</div>,
document.body
)
}
性能优化深度实践
高级缓存策略
// src/lib/advanced-caching.ts
// Next.js 16:unstable_cache 已弃用,改用 "use cache" 指令 + cacheTag/cacheLife 显式缓存
import { revalidateTag, cacheTag, cacheLife } from 'next/cache'
// 多层缓存策略
export class AdvancedCacheManager {
// L1缓存:内存缓存(请求级别)
private static memoryCache = new Map<string, { data: any; timestamp: number; ttl: number }>()
// L2缓存:Next.js缓存(应用级别,"use cache" 显式声明)
private static async fetchCached(key: string) {
'use cache'
cacheLife('hours') // 约1小时级别的缓存 profile
cacheTag('dynamic')
return await this.fetchFromSource(key)
}
// L3缓存:CDN缓存(边缘级别)
private static cdnCache = new Map<string, { data: any; etag: string }>()
// 智能缓存获取
static async get<T>(key: string, options: {
ttl?: number
useMemory?: boolean
useNext?: boolean
useCDN?: boolean
} = {}): Promise<T | null> {
const {
ttl = 300000, // 5分钟默认TTL
useMemory = true,
useNext = true,
useCDN = false
} = options
// L1:内存缓存
if (useMemory) {
const memoryItem = this.memoryCache.get(key)
if (memoryItem && Date.now() - memoryItem.timestamp < memoryItem.ttl) {
return memoryItem.data
}
}
// L2:Next.js缓存
if (useNext) {
try {
const data = await this.fetchCached(key)
if (data) {
// 回填内存缓存
if (useMemory) {
this.memoryCache.set(key, {
data,
timestamp: Date.now(),
ttl
})
}
return data
}
} catch (error) {
console.error('Next.js cache error:', error)
}
}
// L3:CDN缓存
if (useCDN) {
const cdnItem = this.cdnCache.get(key)
if (cdnItem) {
return cdnItem.data
}
}
return null
}
// 智能缓存设置
static async set<T>(key: string, data: T, options: {
ttl?: number
tags?: string[]
useMemory?: boolean
useNext?: boolean
useCDN?: boolean
} = {}): Promise<void> {
const {
ttl = 300000,
tags = [],
useMemory = true,
useNext = true,
useCDN = false
} = options
// L1:内存缓存
if (useMemory) {
this.memoryCache.set(key, {
data,
timestamp: Date.now(),
ttl
})
}
// L2:Next.js缓存(通过 "use cache" 显式声明)
if (useNext) {
// fetchCached 内部使用 "use cache",写入并预热 Next.js 缓存
await this.fetchCached(key)
}
// L3:CDN缓存
if (useCDN) {
this.cdnCache.set(key, {
data,
etag: this.generateETag(data)
})
}
}
// 缓存失效
static invalidate(tags: string[]): void {
// 失效Next.js缓存(Next.js 16:revalidateTag 需传入 cacheLife profile 作为第二参数)
tags.forEach(tag => revalidateTag(tag, 'max'))
// 清理内存缓存
this.memoryCache.clear()
// 清理CDN缓存
this.cdnCache.clear()
}
// 预热缓存
static async warmup(keys: string[]): Promise<void> {
const promises = keys.map(async (key) => {
try {
await this.get(key)
} catch (error) {
console.error(`Cache warmup failed for key ${key}:`, error)
}
})
await Promise.allSettled(promises)
}
// 从数据源获取
private static async fetchFromSource(key: string): Promise<any> {
// 这里实现实际的数据获取逻辑
const response = await fetch(`https://api.example.com/data/${key}`)
if (!response.ok) {
throw new Error(`Failed to fetch data for key: ${key}`)
}
return response.json()
}
// 生成ETag
private static generateETag(data: any): string {
const hash = btoa(JSON.stringify(data))
return `"${hash}"`
}
// 缓存统计
static getStats() {
return {
memoryCache: {
size: this.memoryCache.size,
items: Array.from(this.memoryCache.entries()).map(([key, item]) => ({
key,
age: Date.now() - item.timestamp,
ttl: item.ttl
}))
},
nextCache: {
// Next.js缓存统计需要通过其他方式获取
},
cdnCache: {
size: this.cdnCache.size
}
}
}
}
// 使用示例
export class DataService {
// 获取用户数据(多层缓存)
static async getUser(id: string): Promise<User | null> {
return AdvancedCacheManager.get(`user:${id}`, {
ttl: 600000, // 10分钟
useMemory: true,
useNext: true,
useCDN: false
})
}
// 获取文章数据(多层缓存)
static async getArticle(slug: string): Promise<Article | null> {
return AdvancedCacheManager.get(`article:${slug}`, {
ttl: 1800000, // 30分钟
tags: ['article'],
useMemory: true,
useNext: true,
useCDN: true
})
}
// 更新数据并缓存
static async updateUser(user: User): Promise<void> {
// 更新数据库
await fetch(`/api/users/${user.id}`, {
method: 'PUT',
body: JSON.stringify(user)
})
// 更新缓存
await AdvancedCacheManager.set(`user:${user.id}`, user, {
ttl: 600000,
tags: ['user'],
useMemory: true,
useNext: true
})
// 失效相关缓存
AdvancedCacheManager.invalidate(['user-list', 'user-profile'])
}
}
流式渲染和 Suspense
// src/components/streaming-components.tsx
import { Suspense } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
// 流式页面组件
export function StreamingPage() {
return (
<div className="container mx-auto py-8">
{/* 立即渲染的头部 */}
<PageHeader />
{/* 流式渲染的主要内容 */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2 space-y-8">
{/* 主要内容 - 高优先级 */}
<Suspense fallback={<MainContentSkeleton />}>
<MainContent />
</Suspense>
{/* 相关内容 - 中优先级 */}
<Suspense fallback={<RelatedContentSkeleton />}>
<RelatedContent />
</Suspense>
</div>
<div className="space-y-8">
{/* 侧边栏 - 低优先级 */}
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</div>
</div>
</div>
)
}
// 页面头部(同步渲染)
function PageHeader() {
return (
<header className="mb-8">
<h1 className="text-4xl font-bold text-gray-900">
流式渲染示例
</h1>
<p className="text-xl text-gray-600 mt-2">
展示Next.js 16的流式渲染能力
</p>
</header>
)
}
// 主要内容(异步渲染)
async function MainContent() {
// 模拟慢速数据获取
const data = await fetchSlowData()
return (
<section className="bg-white rounded-lg shadow p-6">
<h2 className="text-2xl font-semibold mb-4">主要内容</h2>
<div className="prose max-w-none">
{data.map((item: any, index: number) => (
<div key={index} className="mb-4">
<h3 className="text-lg font-medium">{item.title}</h3>
<p>{item.content}</p>
</div>
))}
</div>
</section>
)
}
// 相关内容(异步渲染)
async function RelatedContent() {
const relatedData = await fetchRelatedData()
return (
<section className="bg-white rounded-lg shadow p-6">
<h2 className="text-2xl font-semibold mb-4">相关内容</h2>
<div className="space-y-4">
{relatedData.map((item: any, index: number) => (
<div key={index} className="flex items-center space-x-4 p-3 border rounded">
<div className="w-12 h-12 bg-gray-200 rounded"></div>
<div>
<h4 className="font-medium">{item.title}</h4>
<p className="text-sm text-gray-600">{item.excerpt}</p>
</div>
</div>
))}
</div>
</section>
)
}
// 侧边栏(异步渲染)
async function Sidebar() {
const sidebarData = await fetchSidebarData()
return (
<aside className="space-y-6">
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">侧边栏</h3>
<div className="space-y-3">
{sidebarData.map((item: any, index: number) => (
<div key={index} className="p-3 bg-gray-50 rounded">
<h4 className="font-medium text-sm">{item.title}</h4>
<p className="text-xs text-gray-600 mt-1">{item.count} 项</p>
</div>
))}
</div>
</div>
</aside>
)
}
// 骨架屏组件
function MainContentSkeleton() {
return (
<section className="bg-white rounded-lg shadow p-6">
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-1/4"></div>
<div className="space-y-2">
<div className="h-4 bg-gray-200 rounded"></div>
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
<div className="h-4 bg-gray-200 rounded w-4/6"></div>
</div>
<div className="h-4 bg-gray-200 rounded w-1/4"></div>
<div className="space-y-2">
<div className="h-4 bg-gray-200 rounded"></div>
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
</div>
</div>
</section>
)
}
function RelatedContentSkeleton() {
return (
<section className="bg-white rounded-lg shadow p-6">
<div className="animate-pulse space-y-4">
<div className="h-6 bg-gray-200 rounded w-1/4"></div>
<div className="space-y-3">
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center space-x-4">
<div className="w-12 h-12 bg-gray-200 rounded"></div>
<div className="flex-1 space-y-2">
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
<div className="h-3 bg-gray-200 rounded w-1/2"></div>
</div>
</div>
))}
</div>
</div>
</section>
)
}
function SidebarSkeleton() {
return (
<aside className="space-y-6">
<div className="bg-white rounded-lg shadow p-6">
<div className="animate-pulse space-y-4">
<div className="h-6 bg-gray-200 rounded w-1/4"></div>
<div className="space-y-3">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="p-3 bg-gray-100 rounded">
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-3 bg-gray-200 rounded w-1/4"></div>
</div>
))}
</div>
</div>
</div>
</aside>
)
}
// 数据获取函数
async function fetchSlowData() {
// 模拟慢速网络请求
await new Promise(resolve => setTimeout(resolve, 2000))
return [
{ title: '第一部分内容', content: '这是第一部分的详细内容...' },
{ title: '第二部分内容', content: '这是第二部分的详细内容...' },
{ title: '第三部分内容', content: '这是第三部分的详细内容...' }
]
}
async function fetchRelatedData() {
await new Promise(resolve => setTimeout(resolve, 1500))
return [
{ title: '相关文章1', excerpt: '相关文章的简短描述...' },
{ title: '相关文章2', excerpt: '相关文章的简短描述...' },
{ title: '相关文章3', excerpt: '相关文章的简短描述...' }
]
}
async function fetchSidebarData() {
await new Promise(resolve => setTimeout(resolve, 1000))
return [
{ title: '分类1', count: 10 },
{ title: '分类2', count: 8 },
{ title: '分类3', count: 15 },
{ title: '分类4', count: 6 }
]
}
企业级构建优化
Turbopack 配置优化
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Turbopack配置(Next 16:顶层 turbopack 键;experimental.turbo 仅为兼容别名)
turbopack: {
rules: {
// SVG文件处理
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
// 服务器组件优化(Next 15+ 移至顶层 serverExternalPackages)
serverExternalPackages: [
'sharp',
'canvas',
'jsdom'
],
experimental: {
// 优化选项
optimizeCss: true,
optimizeServerReact: true,
optimizePackageImports: [
'lucide-react',
'@radix-ui/react-icons',
'date-fns',
'clsx',
'tailwind-merge'
],
},
// Webpack配置(fallback)
webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) {
// 生产环境优化
config.optimization = {
...config.optimization,
splitChunks: {
chunks: 'all',
cacheGroups: {
// React相关
react: {
test: /[/]node_modules/[/]/,
name: 'react-vendor',
chunks: 'all',
priority: 20,
},
// Next.js相关
next: {
test: /[/]node_modules[/]next[/]/,
name: 'next-vendor',
chunks: 'all',
priority: 15,
},
// UI库
ui: {
test: /[/]node_modules/[/]/,
name: 'ui-vendor',
chunks: 'all',
priority: 10,
},
// 工具库
utils: {
test: /[/]node_modules/[/]/,
name: 'utils-vendor',
chunks: 'all',
priority: 5,
},
// 公共代码
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true,
priority: 1,
},
},
},
}
}
return config
},
// 图片优化
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
imageSizes: [32, 48, 64, 96, 128, 256, 384], // Next 16:16 已从默认 imageSizes 移除
minimumCacheTTL: 60,
dangerouslyAllowSVG: true,
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
// 压缩配置
compress: true,
// 电源配置
poweredByHeader: false,
// 实验性功能
logging: {
fetches: {
fullUrl: false,
},
},
}
module.exports = nextConfig
总结
本章提供的是高级能力的决策框架,不是“已经具备企业级能力”的证明。完成以下验收后,才可以说明你已在一个具体项目中掌握了对应边界:
架构验收
- 为一个页面标注 App Router 的加载、错误、未找到和直接访问行为,并用浏览器复现
- 解释每个组件为何在服务端或客户端运行,确认秘密和不必要依赖没有进入浏览器产物
- 仅在真实导航问题需要时引入并行/拦截路由,并测试刷新、深链接和后退行为
- 在慢数据与失败数据下验证 Suspense/流式边界的可见状态和可恢复结果
性能验收
- 为每个缓存读取记录键、TTL、失效者和写后读取结果;不同身份或租户不能共享错误的数据
- 在固定设备、网络和数据量下保存优化前后指标与 bundle 报告,确认改动解决了目标瓶颈
- 构建命令、依赖锁定和部署环境可以从空环境复现,并能定位失败日志
交付验收
- 对一条受保护写操作验证服务端授权、运行时输入校验、事务失败和幂等重试
- 为外部依赖超时、缓存不可用和发布回退给出用户可见结果及运行记录
- 通过日志、指标与追踪从请求 ID 找到一次失败的入口、依赖和恢复动作
下一步学习
- 深入学习边缘计算和 Serverless 架构
- 掌握微前端和模块联邦
- 学习 GraphQL 和 Apollo 集成
- 探索 AI/ML 集成和智能应用
- 了解 WebAssembly 和性能极限优化
相关资源链接
官方资源
技术文章
工具和资源
模块内相关文档
同模块相关文档
- React 19 深度集成 – 学习 React 19新特性与 Next.js 16的集成实践
- 全栈开发模式 – 掌握现代全栈应用开发架构模式
- 性能优化策略 – 深入 Next.js 应用性能调优技术
相关知识模块
- 测试相关模块 – Next.js 应用的单元测试实践
- 部署相关模块 – Next.js 应用的 Vercel 企业级部署
基础前置知识
- React 基础概念 – React 核心概念回顾
- JavaScript 现代特性 – 现代 JavaScript 语法特性
- TypeScript 入门 – TypeScript 类型系统基础
总结
核心技术要点
- App Router 架构优势: 基于 React Server Components 的新一代路由系统,提供更好的性能和开发体验
- Server Components: 服务端渲染组件,减少客户端 JavaScript 负担,提升首屏加载速度
- Server Actions: 简化数据 Mutations 操作,提供更好的类型安全和开发体验
- 现代化构建系统: Turbopack 和 SWC 提供极速的构建和热重载体验
- 性能优化特性: 图片优化、字体优化、自动代码分割等内置优化功能
学习成果自检
- [ ] 理解 Next.js 16的 App Router 架构和 Pages Router 的区别
- [ ] 掌握 Server Components 和 Client Components 的使用场景
- [ ] 能够使用 Server Actions 处理表单提交和数据变更
- [ ] 熟练运用 Next.js 16的性能优化特性
- [ ] 能够独立创建和部署一个完整的 Next.js 16应用
贡献与反馈
贡献指南
欢迎提交 Issue 和 Pull Request 来改进本模块内容!请遵循以下步骤:
- Fork 本仓库
- 创建特性分支 (
git checkout -b feature/amazing-feature) - 提交变更 (
git commit -m 'Add amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 创建 Pull Request
反馈渠道
- 问题反馈: GitHub Issues
- 内容建议: Discussion 板块
- 技术交流: 欢迎提交 PR 或 Issue 参与讨论
贡献者
- Hello Team – 核心内容开发
- 社区贡献者 – 内容完善和纠错
📜 文档版本: v1.0.0📅 最后更新: 2026年9月🏷️ 标签: #nextjs16 #react19 #app-router #server-components #modern-web⭐ 推荐指数: ⭐⭐⭐
💡 提示: 本模块为 Next.js 16核心模块,建议先掌握 React 基础后再进行学习。结合实践项目能更好地理解概念。
🎯 学习建议: – 建议学习周期: 2-3周 – 理论与实践时间比例: 3:7 – 重点掌握 App Router 和 Server Components – 结合官方文档和社区资源深入学习