这种 Next.JS 文件夹结构推荐吗?

98
我们已经采用了以下项目结构。
|- pages
    |- <page_name>
        |- index.js             # To do a default export of the main component
        |- MainComponent.jsx    # Contains other subcomponents
        |- main-component.css   # CSS for the main component
        |- OtherComponents.jsx  # more than 1 file for child components that are used only in that page
        |- __tests__            # Jest unit and snapshot tests
|- components
    |- index.js                 # Exports all the default components of each component as named exports
    |- CommonCmpnt1
        |- CommonCmpnt1.jsx
        |- common-cmpnt-1.css
        |- index.js             # To default export the component
        |- __tests__
    |- CommonCmpnt2
        |- CommonCmpnt2.jsx
        |- common-cmpnt-2.css
        |- index.js             # To default export the component
        |- __tests__

需要澄清的是,没有任何东西损坏了,它的工作非常出色。我们有一些组件在组件目录中被多个页面重复使用,导入方式如下:

// Assuming we are inside MainComponent.jsx
// ...
import { CommonCmpnt1 } from '../../components';    // We even take it a step further and use absolute imports

此外,仅使用一次的组件位于其 pages 文件夹中。

我们现在唯一面临的问题是热模块重新加载已经失效了,即每当我们编辑 pagescomponents 目录中的 .jsx 文件时,我们必须手动重新加载页面以查看更改(不影响 CSS 文件)。这是我们习惯了的事情,所以并没有对我们产生严重影响。

我的问题是,是否存在我们不知道的任何迫在眉睫的灾难?


4
下一个项目中肯定不会使用通用的CSS,他们将使用基于JavaScript的CSS,例如styled components - Code Maniac
@CodeManiac 我同意,但这更多是一种偏好而不是解决方案。我们在styled-jsx(样式组件),sasscss之间交替使用。 - cross19xx
12个回答

145

在搜索适合NextJS的文件夹结构时,偶然发现了这篇文章。我一直在使用类似的结构,但最近发现这不是使用NextJS的正确方法。

我不太了解细节,但是NextJS在页面级别进行优化。当你构建一个NextJS项目时,你会看到页面作为构建的一部分被记录下来。 NextJS将 pages 文件夹下的每个组件文件都视为页面,因此如果将非页面组件放置在pages文件夹中,那么构建时间将大幅度增加,因为现在NextJS会将每一个这些组件都构建为页面。


1
我同意...相信我,我是吃过亏才明白的。我还没有对这篇文章进行编辑,但是您的编辑将不胜感激。 - cross19xx
2
是的...我也是吃了亏才发现,一直在想为什么构建需要几分钟 :o - Hans
2
我同意这个观点。我也是通过艰苦的实践学到的。现在,我把页面视为路由,并且只放置需要在服务器上呈现的内容,并从组件文件夹动态导入所有内容。这样可以轻松地切换到非 Next.js 项目。 - apnerve
那么正确的解决方案是什么?你的回答表明组件不应该放在“pages”下面,那它们应该放在哪里呢? - Rylan Schaeffer
1
@RylanSchaeffer,你可以将它们添加到common文件夹中。Pages文件夹只应该托管页面而不是组件。如果你把一个组件放在pages文件夹里面,比如: pages/ home/ myComponent.tsx 它会生成一个像localhost:3000/home/myComponent这样的页面。 - Shazil Sattar

53
更新2023年: 自从Next.js引入了新的应用程序目录和新的文件位置改进后,您可以根据自己的需求调整我之前提出的项目组织结构建议。 NextJS文档中的项目组织和文件位置 我喜欢这篇文章中提出的结构。

https://medium.com/@pablo.delvalle.cr/an-opinionated-basic-next-js-files-and-directories-structure-88fefa2aa759

/root
  \_ /.next/
  \_ /components/
      \_ Button/
          \_ button.spec.jsx
          \_ button.styles.jsx
          \_ index.jsx
  \_ /constants/
      \_ theme.js
      \_ page.js
  \_ /contexts/
      \_ Locale/
         \_ index.js
      \_ Page/
         \_ index.js
  \_ /pages/
      \_ _app.jsx
      \_ _document.jsx
      \_ about.jsx
      \_ index.jsx
  \_ /providers/
      \_ Locale/
         \_ index.js
      \_ Page/
         \_ index.js
  \_ /public/
      \_ favicon.ico
      \_ header.png
  \_ /redux/
      \_ actions/
         \_ users/
            \_ index.js
         \_ products/
            \_ index.js
      \_ reducers/
         \_ users/
            \_ index.js
         \_ products/
            \_ index.js
      \_ store/
         \_ index.js
      \_ types/
         \_ index.js
  \_ /shared/
      \_ jsons/
          \_ users.json
      \_ libs/
          \_ locale.js
      \_ styles/
          \_ global.css
  \_ /widgets/
      \_ PageHeader/
          \_ index.jsx
  \
  \_ .eslintignore
  \_ .eslintrc
  \_ .env
  \_ babel.config.js
  \_ Dockerfile
  \_ jest.config.js
  \_ next.config.js
  \_ package.json
  \_ README.md

16
如果有人仍然感兴趣,我会根据文件类型在我的项目中保存该文件,例如:
|-Nextjs-root
  |-Components
    |-Header.js
    |-Footer.js
    |-MoreExamples.js
  |-styles
   |-globals.css
   |-header.module.css
   |-footer.module.css
  |-Services
    |-api              #Axios config to connect to my api
  |-Context
   |-AuthContext.js    #Global context to my app for auth purposes
  |-pages
   |-index.js

我怀疑服务器端调试器对于 Services/api 不起作用,因为我猜测 next.js 仅为页面目录内的文件创建源代码映射。 - Nika Tsogiaidze
2
这不具备可扩展性。按功能创建文件夹 对于中大型项目更为适合。 - Spankied

8

我建议使用模块化设计模式:

/public
    favicon.ico
/src
    /common
        /components
            /elements
                /[Name]
                    [Name].tsx
                    [Name].test.ts
        /hooks
        /types
        /utils
    /modules
        /auth
            /api
                AuthAPI.js
                AuthAPI.test.js
            /components
                AuthForm.tsx
                AuthForm.test.ts
            auth.js
    /pages
        /api
          /authAPI
              authAPI.js
          /[Name]API
              [Name]API.js
        _app.tsx
        _document.tsx
        index.tsx

我写了一篇关于这个的文章:https://dev.to/vadorequest/a-2021-guide-about-structuring-your-next-js-project-in-a-flexible-and-efficient-way-472 唯一需要特别注意的是,不要在 `pages` 下添加非实际页面或 API 端点的任何内容(例如:测试、组件等),因为没有办法忽略它们,Next.js 将把它们打包和部署为实际页面。

1
样式方面怎么处理?模块 CSS 文件和全局 CSS 文件呢? - Naor
2
对于组件级别的 CSS 文件,可以将其包含在 .tsx 文件内(使用 CSS-in-JS 时),或者与组件同名的 .css 文件一起放置在同一个文件夹中。 - Vadorequest
对于全局样式,我有一个特殊的 src/app 文件夹,其中包含影响整个应用程序的所有内容。由于我使用 CSS-in-JS,因此我有一个 src/app/components/GlobalStyles.tsx 文件,其中包含所有全局样式(在 @emotion/react 库中的 Global 组件内)。 - Vadorequest
1
你的页面组件放在哪里?我的意思是:pages/about.tsx 很可能会渲染一个 About.tsx 组件。当然,about 通常是一个非常简单的组件,但我们也可以谈论 pages/posts/[id].tsx,它将呈现一个处理动态数据的 PostContainer 等等。由于它们不能在 pages 路由文件夹中,所以你把它们放在哪里?此外,如果可以,请查看:https://github.com/vercel/next.js/discussions/34130 - cbdeveloper
通常在/modules子文件夹中。 - Vadorequest

6

对于仍然感兴趣的所有人,我个人推荐这种方法,因为它有助于将资源分隔开,并且可以快速找到事物和进行单元测试。

它受到HackerNoon上一篇文章的启发,如何构建React应用程序的结构


7
半年过去了,你还在使用这个结构吗?在使用过程中有什么收获吗?感谢您的帮助。 - Scott L
抱歉回复晚了。是的,我学到了很多东西。首先,Next将页面内的所有内容都视为目录,因此它基本上将所有文件视为单独的页面。这在某些情况下可能不一定是安全漏洞,但谨慎起见还是要小心为妙。 - cross19xx
1
这就是我担心的,如果你的MainComponent.jsx文件和OtherComponents.jsx文件在pages文件夹下面,那么如果你在URL中输入它,它不会显示页面吗? - hellomello
它将会这样做。例如,如果页面名称是“about”,那么访问/about、/about/index都可以正常工作。唯一的缺点是,如果您键入/about/OtherComponent,它也会尝试呈现其他组件。 - cross19xx
我更愿意使用类似这样的东西:https://dev.to/vadorequest/a-2021-guide-about-structuring-your-next-js-project-in-a-flexible-and-efficient-way-472 - Vadorequest
不太确定为什么添加名称“bundles”特别有用。你会有多个bundle吗?我觉得不会。所有东西都在src中,这在大多数npm包中都是标准的。拥有一个common/文件夹可能对于非常见项目的分离是可以的,但除此之外,“components”文件夹基本上是不言自明的。 - Steve Tomlin

6

我仅使用pages目录进行路由目的

这样你可以保持一个干净的目录结构并让组件靠近其父级组件

src
├── shared-components
│   └── ...
├── pages-components
│   ├── page-1
│   │   ├── index.tsx
│   │   ├── FooComponent.tsx  // page-1 specefic component
│   │   └── ...
│   └── home-page
│       └── index.tsx
└── pages                     // just for routing
    ├── page-1               
    │   └── index.ts          // export default from 'src/page-components/page-1'
    └── index.ts              // export default from 'src/page-components/home-page'

5

没有一种正确的方法来定义你的结构。 这是我定义的方式

  • src
    • components
      • layout.js --- 放在components文件夹中的一个组件,多个页面将使用此组件
      • templates --- 页面特定组件
        • home -- 首页
          • article.js -- 文章组件
      • ui --- 样式化组件,例如:Button, Title, Link
    • pages
    • styles
  • public

3

确保将仅后端文件与前端+后端文件分离

无论您如何命名它们,我建议使用具有非常明确语义的两个目录:

  • 仅后端目录:可进行仅针对后端的操作,例如直接访问数据库或文件系统(require('fs'))
  • 前端+后端目录:仅限于前端安全内容。由于Next.js是SSR设置,因此任何前端安全的内容也必须是后端安全的,因此您还可以从后端使用它们,例如共享配置或帮助程序。

这很重要,否则您将开始遇到错误,例如:

找不到模块: 无法解析 'fs'

在使用HoC拆分事物时会出现此类错误,如“在Next.js应用程序中找不到模块:无法解析'fs'”所述,除非明确划分,否则我不知道如何解决此问题。

也许这是常用的lib/components/约定的预期语义,但我找不到明确的引用。

我更倾向于只称它们为back/front/

默认情况下使用ESLint pages/components/lib/

这是我在https://nextjs.org/docs/basic-features/eslint#linting-custom-directories-and-files中提到的仅有的清晰的上游代码影响(当然,pages/是魔法,包含URL入口点,您显然不应将任何其他内容放在那里):

默认情况下,Next.js将运行ESLint以检查pages/components/lib/目录中的所有文件。

在这种情况下,目录列表可以根据文档进行轻松配置。


/server and /client are the standard for /back and /front - mtro
1
@mtro 谢谢,你有参考资料吗? - Ciro Santilli OurBigBook.com

2

目前最适合我的方式是仅使用pages文件夹进行路由,每个文件中的组件只是src文件夹中真正页面组件的包装器。采用这种方法,我可以更轻松地构建我的主页,并且感觉更加直观-将布局及其子组件放在同一文件夹中。


0

所有这些答案都非常好,甚至那个被踩的答案也可以用一些澄清。当你要开始一个新的应用程序,并且知道它将会变得相当庞大时,这是一个艰难的决定;你现在定义的模式,你将会被困在其中多年,而这就是目标,好的模式能够长期发挥作用。另一个目标是可翻译性,如果模式需要更改,它不应该看起来像是一个不可能完成的任务。

扩展@Anamol Maharjan的回答。使用这种技术,实际上可以使用传统的TS最佳实践文件夹结构:https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions

通过定义页面的扩展名(甚至可以尝试使用api)。路由是自然的,同时保持了TS最佳实践......主要是按主题分组的想法,而不是按类型分组,仅按类型分组而没有主题在层次结构中会变得有问题。因此,人们应该努力实现一个主题/类型受限的结构,以及一个具有类型定义子文件夹的公共文件夹。

在这方面,人们可以使用这个包来做:

pages/
  api/
  common/
   data/
   views/
     controls/
       Button.jsx
     indicators/
       Badge.jsx
   styles/
  page/
    login/
      data/
        LoginHandler.tsx
        LoginRequest.tsx
      views/
        modals/
          PasswordReset.tsx
      Login.page.tsx

就偏好而言,目前我们的应用程序仅使用 pages dir 作为路由...但是这样会出现 common/pages,我们真的希望将 nextjs 的 pages dir 更名为 routes。所以我们正在讨论这个解决方案。很遗憾,为了选择最佳应用程序,需要进行复杂分析。一旦您开始添加详细的测试及其所有可能的方法、Storybook 和 NextJS 似乎无法包装、配置和推荐的其他所有内容,情况就会变得复杂。

最安全的选项似乎是(没有特定顺序):

  • 模块化方法 > 没有特定于页面的模块,只有全局模块
  • .pages.ts 扩展限制 > 似乎使其成为一个纯 TS 应用程序,并添加了一些功能。它还复制了新的 nextJS 应用程序文件夹,但在我写这篇文章时,它还没有准备好。如果我们有一个配置选项来将页面更改为应用程序并移动 API,我认为就没有必要使用应用程序目录了...那可能就是应用程序目录的全部内容,其余部分都是服务器端的东西,后端功能添加以支持该抽象;希望这也可以弥补接受答案中描述的过度编译问题。
  • 页面容器组件仅在页面目录中 > 特定于页面的测试无法放置在其中,如果您有特定于页面的组件,则会在公共目录中再次出现另一个页面目录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接