NextJS 13 项目文件夹结构最佳实践
问题描述
在使用 NextJS 13 的 App Router 时,开发者常面临以下困惑:
- 如何合理组织路由结构(特别是如登录/注册页面等公共路由)?
- 组件应该存放在何处?如何对相关组件进行分组?
- 如何平衡项目清晰度与文件组织效率?
- 如何利用新版特性优化项目结构?
本文将提供清晰的解决方案和实用示例,帮助你构建可维护的 NextJS 13 项目结构。
核心解决方案
推荐结构:单一 /app
目录 + 组织方案
最受社区认可的方案是将所有项目文件集中在 /app
目录中,通过特殊命名规则区分不同内容:
tree
/app
├── (routes) # 路由分组
│ └── auth
│ ├── login
│ │ └── page.tsx # /auth/login 页面
│ ├── signup
│ │ └── page.tsx # /auth/signup 页面
│ └── layout.tsx # 认证页共享布局
├── _components # 全局组件
├── _lib # 工具库(utils、常量等)
├── layout.tsx # 根布局
└── page.tsx # 首页
关键组织技巧
路由组(Route Groups)
- 使用括号包裹(如
(routes)
)将路由分组 - 不改变实际 URL 路径:
(routes)/auth/login
→/auth/login
- 适合分组相关页面(如所有认证页面)
- 使用括号包裹(如
私有文件夹(Private Folders)
- 下划线前缀命名(如
_components
) - 被 Next.js 路由系统忽略
- 适合存放非路由元素(组件、工具库等)
- 下划线前缀命名(如
布局继承
- 共享
layout.tsx
自动包裹其所在目录下的所有路由 - 示例:
/app/auth/layout.tsx
同时作用于/auth/login
和/auth/signup
- 共享
备选方案:/src
分离结构
如果更倾向业务代码与配置文件分离,可使用官方推荐的 /src
结构:
tree
/src
├── app
│ └── ... # 同上述路由结构
├── components
│ ├── common # 公共组件
│ └── dashboard # 页面专属组件
├── lib
│ ├── constants.ts # 常量
│ ├── utils.ts # 工具函数
│ └── validators.ts # 验证逻辑
└── services # API服务(可选)
实践建议
- 组件存放原则:
- 公用组件:存放于
_components
或/src/components/common
- 页面专属组件:存放于对应路由目录内(如
/app/(routes)/dashboard/CustomComponent.tsx
)
- 公用组件:存放于
关键实践详解
路由共享布局实现
使用目录级别 layout.tsx
创建共享布局:
tsx
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<div className="auth-layout">
<header>认证中心</header>
<main>{children}</main> {/* 子页面内容在此渲染 */}
<footer>© 2023 版权所有</footer>
</div>
);
}
此布局将自动应用到 /auth/login
和 /auth/signup
两个页面。
组件类型区分实践
结合 NextJS 13 的服务端组件特性合理组织:
tree
/app
├── _components
│ ├── ServerCard.tsx # 服务端组件(无交互逻辑)
│ └── ClientButton.tsx # 客户端组件(带'use client'指令)
客户端组件需明确标注:
tsx
'use client';
import { useState } from 'react';
export default function CounterButton() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c+1)}>点击 {count}</button>;
}
重要提醒
NextJS 13 默认所有组件都是服务端组件。只有包含交互逻辑(如 useState
)的组件才需标记为客户端组件。
进阶组织技巧
动态装载文件
tsx// 工具文件可放置在任意位置 export function formatDate(date: Date) { return new Intl.DateTimeFormat('zh-CN').format(date); }
类型集中管理
ts// 集中类型定义(TypeScript) export type User = { id: string; name: string; email: string; };
最佳实践总结
优先选择
/app
+ _私有文件夹 + (路由组) 方案- 项目结构最清晰
- 利用框架原生功能
- 团队协作更直观
核心结构规范:
- 路由页 → 对应路由目录的
page.tsx
- 共享布局 → 目录级
layout.tsx
- 全局组件 →
_components
- 工具函数 →
_lib
- 路由页 → 对应路由目录的
组件组织原则:
通过实施这些实践,你将获得一个可扩展、易维护且符合 NextJS 13 最佳实践的项目结构,同时解决路由定位和组件组织的问题。