去年Vercel内部调研显示,87%的Next.js项目死于同一个原因:不是功能做不出来,是代码长到一定程度后,改一行崩三行。
问题出在开头。大多数人跑完npx create-next-app就开始写页面,等第二个应用冒出来——可能是后台管理、可能是营销站点——才发现自己在三个仓库之间复制粘贴组件,auth逻辑散落在/lib、/helpers、/utils里,没人知道该听谁的。
这不是技术债,是架构债。而且利息比你想的高得多。
App Router的"就近原则":把相关的东西焊在一起
Next.js 13+的App Router做了一件小事,但影响被低估了:它允许你把一个路由需要的所有东西——布局、加载态、错误边界、局部组件、服务端动作——全部塞进同一个文件夹。
以前/pages放页面,/components放组件,数据获取逻辑到处飘。现在/dashboard可以长这样:
page.tsx是入口,layout.tsx管壳子,loading.tsx和error.tsx管生命周期,components/StatsCard.tsx只在这个路由里用——所有东西对号入座,不用翻三个目录去找。
这叫colocation(同地协作),听起来像整理房间,实际上是给代码划地盘。地盘清楚了,依赖关系才能清楚。
按功能切分:别让"通用"变成"混乱"
很多人听到"分层架构"就想到技术分层:/controllers、/services、/repositories。这在Next.js里是反模式。
更实用的切法是按功能(feature-based)。一个功能一个文件夹,里面再按技术角色细分。比如用户认证功能:
features/auth/里面,api/放服务端动作,components/放UI,hooks/放逻辑,types.ts把类型焊死。别的功能想调auth?只能走暴露出来的接口,不能伸手进内部乱翻。
这种结构 onboarding 一个新成员,告诉他"所有用户相关的东西在features/auth",比让他猜"这个util是在/lib还是/helpers"快十倍。
Turborepo:多应用时代的"共享厨房"
单应用架构再干净,遇到多应用场景也会崩。你的主站、后台、移动端API,80%的业务逻辑是重复的,但直接复制仓库会让更新变成噩梦。
Turborepo的解法是:把共享代码抽成内部包(internal packages),放在packages/目录下,各应用按需引用。
关键细节:共享包必须显式声明依赖。ui包需要react?写在peerDependencies里,版本冲突时Turbo会喊你,而不是运行时默默崩掉。
CI阶段,Turbo只构建和测试真正变更过的包。一个按钮样式改了,不需要跑完整套端到端测试——这个缓存策略在大仓库里能省掉几十分钟的流水线时间。
Server Components:画一道数据获取的"国境线"
Next.js的Server Components(服务端组件)被很多人用成了"能跑在服务器上的组件",浪费了它真正的价值:数据获取边界的声明。
理想的分工是:Server Components负责拿数据、做权限校验、决定渲染什么;Client Components(客户端组件)只处理交互和浏览器API。中间用props传数据,而不是让客户端组件自己去fetch。
这条边界画清楚了,你才能知道一个组件为什么慢——是数据库查询问题,还是客户端hydration太重。
测试策略也要跟着分层走。Server Components用Node环境测数据逻辑,Client Components用浏览器环境测交互,共享包用最快的方式测纯函数。别用一把锤子敲所有钉子。
一个能抄的作业:完整目录结构
把以上几层叠在一起,一个可扩展的Next.js仓库长这样:
apps/web(主站)、apps/admin(后台)、packages/ui(共享组件)、packages/auth(共享逻辑)、packages/database(数据库封装)。每个apps里的路由按功能组织,packages里的代码显式暴露接口。
CI配置里,Turbo的pipeline声明哪些任务可以并行、哪些需要缓存、哪些必须串行。不是"先install再build再test"的固定剧本,而是根据变更动态调整。
这套结构Vercel自己用了两年,但官方文档里藏得很散。大多数人看到时,项目已经长到搬不动了。
你的项目现在在第几层?是还在复制粘贴组件,还是已经能无痛新增应用了?
热门跟贴