是否有基于React的项目的官方样式指南或命名约定?

时间:2019-03-18 12:29:24

标签: javascript reactjs naming-conventions

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

我已经在React项目中看到了常见的模式和命名模式:

  1. 非反应文件夹和文件:camelCasekebab-case
  2. 反应(在components文件夹中):PascalCase

react中是否有用于文件夹/文件命名的正式约定?如果没有,是否有基于该模式的样式指南?还是为什么大多数人都使用这个原因?

5 个答案:

答案 0 :(得分:3)

没有官方指南。 大多数项目采用PascalCase作为React组件的原因是为了模仿该文件的主要输出。 React组件按惯例是PascalCased的,并且在使用jsx时,pascal大小写成为强制性的(实际上只有大写的第一个字母成为强制性的)。 通常,其余文件的cameCase或kebab-case遵循的也是通常更常见的javascript项目首选项。

答案 1 :(得分:2)

没有React的官方样式指南。但是您可以对AirBnb的React使用最受欢迎的eslint配置。

在这里https://github.com/airbnb/javascript/tree/master/react

了解更多

答案 2 :(得分:1)

在许多语言中,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

答案 3 :(得分:1)

只加我的两分钱。正如其他人所说,文件结构是不受限制的。但是,组件命名不是。他们应该应该PascalCase,以便React知道您是否使用的是functionclass或HTML element†。

例如:

class input extends Component {...}

!为什么?因为React不知道您是要使用input元素还是基于类的组件。

这就是为什么您会看到PascalCase组件的原因:

class Input extends Component {...}

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

component / 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文件,因为“ index.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>
);

为什么?因为在测试时,该功能将在Jest中显示为:

<_default />

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

<_default />
<_default />
<_default />
  • 避免冗长的文件(150行或更少的行数),因为这会造成读取/理解的痛苦,甚至使调试变得更加痛苦。

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

什么更容易阅读:

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

Example #2(每行318行)

示例1模仿读书。粘贴在一起的多个页面可提供易于阅读的体验。与示例#2相比,它读起来像一英里长的滚动条,很容易迷路!

  • 样式表可以是蛇形或驼峰式。

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

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 ===“保持简单,愚蠢!”

答案 4 :(得分:0)

此刻,我有一个以PascalCase命名的文件夹,并且其中有一个index.js文件-这是我的组件。

我直接附加到根组件的任何组件都已使用其自己的index.js嵌套在其自己的Folder中。我还使用点表示法来描述与该文件夹直接相关的任何文件的性质,例如[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商店,因为我的商店模块不太可能具有真正的深层文件夹结构,所以我只有一个Root模块文件夹,其中通常有两个js文件Actions.jsindex.js索引是我的主要商店类,它扩展了我的Actions类。 (我发现一个具有observablecomputedaction属性的mobx类有点混乱)。

Store文件夹本身有一个index.js,它会依次导入所有同级商店模块,以将它们组合到一个商店对象中(这是我的项目所需要的)

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

我认为没有真正正确的方法,因为它取决于首选项,上面的方法我认为可读,并且在VSCode上使用工具查找文件时,在搜索“我想查看所有是常量文件” 搜索constants.[component name]