【问题标题】:Is there an official style guide or naming convention for React based projects?是否有基于 React 的项目的官方风格指南或命名约定?
【发布时间】:2019-08-08 19:48:36
【问题描述】:

我正在与我的团队建立一个 React 项目,该项目将使用 mobX 作为状态管理器以及 TypeScript。

我在 React Projects 中看到了一种常见的大小写和命名模式:

  1. 非 React 文件夹和文件:camelCase 或 kebab-case
  2. React(在components 文件夹内):PascalCase

react 中是否有正式的文件夹/文件命名约定?如果没有,是否有此模式所基于的样式指南?或者是大多数时候使用这个的原因?

【问题讨论】:

  • 我不喜欢对包含 React 代码的文件和目录采用不同的命名约定。整个项目的命名应该是一致的,任何库都不应该对此产生任何影响。
  • @Ruid'Orey 我将其精简到您问题的关键是确保基于意见的部分的痕迹消失,以便它可以保持开放。

标签: javascript reactjs naming-conventions


【解决方案1】:

目前我有一个以 PascalCase 命名的文件夹,其中我有一个 index.js 文件 - 这是我的组件。

任何直接附加到根组件的组件,我都使用自己的 index.js 嵌套在自己的文件夹中。我还使用点符号来描述与该文件夹直接相关的任何文件的性质,例如 [descriptor].[name].[prefix]

Components/
    ComponentName/
    |---util.componentName.js
    |---constants.componentName.js
    |---styles.componentName.scss
    |---index.js
        ChildComponent1/
        |---util.childComponent1.js
        |---styles.childComponent1.scss
        |---index.js
        ChildComponent2/
        |---util.childComponent2.js
        |---styles.childComponent2.scss
        |---index.js

对于我的 mobx 商店,因为我的商店模块不太可能拥有真正的深层文件夹结构,所以我有一个根模块文件夹,其中通常包含两个 js 文件 Actions.js 和 index.js 索引作为我的主要商店类,它扩展了我的 Actions 类。 (我发现一个具有observable、computed 和action 属性的 mobx 类有点混乱)。

Store 文件夹本身有一个 index.js,它导入所有同级存储模块,以便稍后将它们组合成一个存储对象(我的项目需要)

Store/
    StoreModule/
    |---actions.js
    |---index.js
    AnotherStoreModule/
    |---actions.js
    |---index.js
    index.js

我想没有真正正确的方法,因为它取决于偏好,我发现上面的方法是可读的,当使用 VSCode 上的工具查找文件时,它可以更容易地搜索诸如“我想查看所有文件是常量文件”搜索constants.[component name]

【讨论】:

  • 我不觉得将组件名称保留在文件中是值得的。您也可以使用 ComponentName/styles.js 进行搜索
  • @fard 这一切都需要解释,但是当我在我的 VS 代码中使用 command P 来查找文件名时,我发现将文件名格式化为 styles.[filename] 很有用,尤其是作为项目我目前正在研究的是 React-Native,我们的样式文件是 js 文件,当我试图找到正确的文件而不是寻找 scss 后缀时,它让我更清楚
【解决方案2】:

在许多语言中,PascalCase 他们的类是很常见的,并且有camelCase 函数和变量名。一个JS例子,

function hello() {console.log('world')};

class Foo {
  say() { console.log('bar') }
}
let foo = new Foo();
foo.say();

组件通常是类class Nav extends React.PureComponent,因此逻辑连接是类似地命名包含该类的文件,从而匹配案例导入语句import Nav from './Nav

您可能还有一个实用程序文件,它导出一个函数,而不是一个类。同样,很高兴有匹配的案例import hello from './hello'

因此,您可能会发现一个常见的结构,例如

src
- App.js
- components/
  - Nav.js
- util/
  - hello.js

【讨论】:

  • 谢谢,但如果在 React 中该模式是正式的官方模式,我的问题更多。因为大多数 React 项目似乎使用它而不是其他模式/约定。尽管反应被认为是无主见的。并尝试理解为什么在 React 中会这样。
  • @Ruid'Orey React 是一个 Facebook 项目。据我所知,他们没有明确的“你必须做这种风格”的指导方针。但是,如果我们所有人都使用自己的设备,我们可能会弄得一团糟。 AirBnB 有一个样式指南,可能会有所帮助,但具体来说是一个正式的模式?从来没听说过。 github.com/airbnb/javascript/tree/master/react
【解决方案3】:

没有官方指南。 大多数项目将 PascalCase 用于 React 组件的原因是模仿该文件的主要导出。 React 组件按约定是 PascalCased,当使用 jsx 时,pascal 大小写成为强制性的(实际上只有大写的第一个字母成为强制性的)。 其余文件的 cameCase 或 kebab-case 只是遵循一般 javascript 项目更常见的偏好。

【讨论】:

    【解决方案4】:

    React 没有官方的风格指南。但是你可以使用 AirBnb 的 React 最流行的eslint 配置。

    在此处阅读更多信息https://github.com/airbnb/javascript/tree/master/react

    【讨论】:

      【解决方案5】:

      只是为了增加我的两分钱。正如其他人所说,文件结构是无主见的。但是,组件命名不是。他们应该是PascalCase,以便 React 知道您使用的是function、class 还是HTMLelement†。

      例如:

      class input extends Component {...}
      

      糟糕!为什么?因为 React 不知道你是在尝试使用 input 元素还是基于类的组件。

      这就是你会看到 PascalCase 组件的原因:

      class Input extends Component {...}
      

      † 有一个例外,您可以使用dot notation。例如,如果您有多个导出并将它们全部导入为 fields,那么您可以执行以下操作:

      组件/字段/index.js

      import React, { Component } from 'react';
      
      export class input extends Component {
        state = { value: "" };
      
        handleChange = ({ target: { value } }) => {
          this.setState({ value });
        };
      
        render = () => (
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        );
      }
      
      export class textarea extends Component {
        state = { value: "" };
      
        handleChange = ({ target: { value } }) => {
          this.setState({ value });
        };
      
        render = () => (
          <textarea
            type="text"
            value={this.state.value}
            onChange={this.handleChange}
          />
        );
      }
      

      components/App/index.js

      import React, { Fragment } from 'react';
      import * as fields from "../fields";
      
      const App = () => (
        <Fragment>
           <fields.input />
           <fields.textarea />
         <Fragment>
      );
      
      export default App;
      

      作为一般经验法则,我完全避免使用dot notation。它感觉很笨拙,并且可能会使其他不知道fields 结构的开发人员感到困惑。另外,我不喜欢在一个文件中堆叠多个组件,然后将它们作为一堆导入。此外,该文件可能会变得非常大,并且导航和调试起来很麻烦(下面将详细介绍)。


      也就是说,为了保持结构简单,我喜欢将主目录保持小写:

      ├── dist // compiled application files to be served
      |   ├── css
      |   |   ├── main.[contenthash:8].css
      |   |   └── main.[contenthash:8].css.map
      |   ├── js
      |   |   ├── main.[hash].js // depending on app size, this may contain multiple js files for code splitting
      |   |   └── main.[hash].js.map
      |   ├── media
      |   |   └── [hash].[ext] // static assets like fonts and images
      |   └── favicon.ico
      |   └── index.html
      |
      ├── config // supporting "webpackdevserver" configuration files
      |   ├── devServer.js
      |   ├── envs.js
      |   ├── optimization.js
      |   ├── output.js
      |   ├── paths.js
      |   ├── plugins.js
      |   └── rules.js
      |
      ├── public
      |   ├── favicon.ico
      |   └── index.html
      |
      ├── src
      |   ├── actions // redux actions
      |   ├── components // stateful and stateless reusable components that just display "stuff" -- stateful components change and manipulate the UI
      |   ├── containers // stateful components that utilize the reusable "components" to CRUD data and/or are connected to redux
      |   ├── images
      |   ├── pages // utilize components/containers to display something when visiting a "/route"
      |   ├── reducers // redux reducers
      |   ├── root // aka "<App />" that combines "routes", redux and other top-level supporting files into one place
      |   ├── routes // assigns "pages" to a "/route"
      |   ├── styles // shared and/or global styles used by all "components"
      |   ├── types // redux types
      |   ├── utils // supporting app files: like test setup, custom polyfills, axios configurations, ...etc
      |   └── index.js // a simple file that "ReactDOM.render"s the "App"
      |
      ├── server.js // express setup to serve the "dist" folder
      └── webpack.config.js
      

      然后在 component 文件夹中,我将 PascalCase 我的组件表示如下:

      └── components
          └── Input
              ├── __tests__
              |   └── Input.test.js // jest unit tests for "index.js"
              ├── index.js // all required code/styles to be exported
              └── styles.scss // styles required by "index.js"
      

      为什么是这种结构?

      • 可重复使用的组件,可随时随地使用。
      • 与Input 相关的所有内容都包含在此文件夹中。 因此,我可以将其交给某人,他们可以将其插入到他们的应用程序中并直接使用它。
      • Webpack 已设置为自动导入 index.js,因此无需遍历大量嵌套文件即可轻松导入:import Input from 'components/Input';(此外,无需指定要使用的确切 js 文件,因为“索引.js" 包含所有必需的代码)。

      缺点:

      • 你会有很多小文件夹。
      • 编译错误都将包含 index.js 命名法,因此起初可能会有点混淆“index.js”失败的原因。

      我以前做的另一种方法是:

      └── components
          ├── input // lowercase name to delineate it's a "pure" function -- the actual function will be a PascalCased "Input"
          |   ├── input.test.js // jest unit tests for "input.js"
          |   ├── input.js // all required code/styles to be exported
          |   └── styles.scss // styles required by "input.js"
          |
          └── Sidebar // PascalCase because it's a "class"
              ├── Sidebar.test.js // jest unit tests for "Sidebar.js"
              ├── Sidebar.js // all required code/styles to be exported
              └── styles.scss // styles required by "Sidebar.js"
      

      为什么是这种结构?

      • 可重复使用的组件,可随时随地使用。
      • 与Input 相关的所有内容都包含在此文件夹中。 因此,我可以将其交给某人,他们可以将其插入到他们的应用程序中并直接使用它。
      • 取决于主文件夹,它描述了组件是function 还是class。
      • 当发生编译错误时,我确切地知道是哪个文件导致了错误。

      缺点:

      • 你会有很多小文件夹。
      • 有时组件可能会从有状态变为无状态(反之亦然),因此如果您严格遵守此命名模式,则必须更新主文件夹以反映更改,这意味着您还需要更新使用此组件的任何其他文件的路径。
      • 导入可能看起来有点多余和冗长:import Input from 'components/input/input.js';

      其他一般准则:

      • 避免默认导出匿名函数:

      默认导出匿名函数示例:

      export default () => (
        <p>Anonymous Function</p>
      );
      

      为什么?因为在测试时,函数会在酶中显示为:

      <_default />
      

      当您在一个组件中有多个匿名函数时,哪个是哪个!?

      <_default />
      <_default />
      <_default />
      
      • 避免使用冗长的文件(150 行或更少),因为阅读/理解会很痛苦,调试会更痛苦。

      通常情况下,我发现大多数组件在经过适当优化后会低于 100 行左右。最坏的情况是我必须创建小的子组件来补充主要组件。但!更容易阅读和调试。

      什么更容易阅读:

      Example #1(34 行带有补充子组件)

      Example #2(318 行)

      示例 #1 模拟阅读一本书。将多个页面粘合在一起时会产生易于阅读的体验。与示例 #2 相比,它读起来像一英里长的卷轴,很容易迷路!

      • 样式表可以是snake-case 或camelCase。

      这可能会令人困惑,但这完全取决于您如何应用样式。如果您只是像这样导入样式:

      import "./styles.css";
      

      然后你可以使用蛇形盒:

      <input className="snake-case" type="text" value="" onChange={this.handleChange} />
      

      但是,如果您使用的是css modules,那么您将需要使用 camelCase:

      import { camelCaseClassName } from "./styles.css";
      

      为什么?因为捆绑器(如 Webpack)不支持蛇形导入:

      <input className={camelCaseClassName} type="text" value="" onChange={this.handleChange} />
      

      结论:创建文件夹结构的方法有很多种,其中包含一些技巧和窍门来保持逻辑流程。只需选择一个最适合您并且不会干扰您旁边工作的人!

      换句话说,K.I.S.S === “保持简单,傻瓜!”

      【讨论】:

      • 非常感谢,这对我来说更清楚了。只是为了澄清,所以在组件名称上使用 PascalCase 的原因是组件的第一个字母不是小写字母,并且与“本机”关键字混淆......就这样吗?谢谢!
      • 如果原生的意思是 HTML 元素,如 div、p、input 等等,那么是的,你应该使用 PascalCase 将你的组件与这些 elements 分开.同样,如果一个组件和一个元素共享相同的名称——例如,你有一个名为 div 的类——那么 React 应该如何知道要渲染哪个!?您是指类还是指元素?简而言之,PascalCase 有助于避免这些命名冲突。此外,React 将小写名称视为元素(这也包括 camelCase 名称)!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-24
      • 2019-06-05
      • 2015-02-19
      • 1970-01-01
      • 2021-11-10
      • 2012-04-18
      • 1970-01-01
      相关资源
      最近更新 更多