Skip to content

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                # 首页

关键组织技巧

  1. 路由组(Route Groups)

    • 使用括号包裹(如 (routes))将路由分组
    • 不改变实际 URL 路径:(routes)/auth/login/auth/login
    • 适合分组相关页面(如所有认证页面)
  2. 私有文件夹(Private Folders)

    • 下划线前缀命名(如 _components
    • 被 Next.js 路由系统忽略
    • 适合存放非路由元素(组件、工具库等)
  3. 布局继承

    • 共享 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)的组件才需标记为客户端组件。

进阶组织技巧

  1. 动态装载文件

    tsx
    // 工具文件可放置在任意位置
    export function formatDate(date: Date) {
      return new Intl.DateTimeFormat('zh-CN').format(date);
    }
  2. 类型集中管理

    ts
    // 集中类型定义(TypeScript)
    export type User = {
      id: string;
      name: string;
      email: string;
    };

最佳实践总结

  1. 优先选择 /app + _私有文件夹 + (路由组) 方案

    • 项目结构最清晰
    • 利用框架原生功能
    • 团队协作更直观
  2. 核心结构规范

    • 路由页 → 对应路由目录的 page.tsx
    • 共享布局 → 目录级 layout.tsx
    • 全局组件 → _components
    • 工具函数 → _lib
  3. 组件组织原则

通过实施这些实践,你将获得一个可扩展、易维护且符合 NextJS 13 最佳实践的项目结构,同时解决路由定位和组件组织的问题。