基于 React 的项目是否有官方的风格指南或命名约定?
Is there an official style guide or naming convention for React based projects?
我正在与我的团队建立一个 React 项目,该项目将使用 mobX 作为状态管理器,外加 TypeScript。
我在 React 项目中看到了一种常见的大小写和命名模式:
- 非 React 文件夹和文件:
camelCase
或 kebab-case
- React(在
components
文件夹内):PascalCase
folder/file React 中是否有正式的命名约定?如果没有,是否有此模式所基于的风格指南?或者为什么这个被用得最多的原因?
目前我有一个以 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
索引是我的主要商店 Class,它扩展了我的操作 class。 (我发现一个具有 observable
、computed
和 action
属性的 mobx class 有点混乱)。
Store 文件夹本身有一个 index.js
它导入所有同级商店模块以便稍后将它们组合成一个商店对象(我的项目需要)
Store/
StoreModule/
|---actions.js
|---index.js
AnotherStoreModule/
|---actions.js
|---index.js
index.js
我想没有真正正确的方法,因为它取决于偏好,我觉得上面的方法是可读的,并且在 VSCode 上使用工具查找文件时,它可以更容易地搜索 "i want to see all files that are constants files" 搜索 constants.[component name]
PascalCase 在它们的 classes 中很常见,并且具有驼峰式函数和变量名。一个 JS 例子,
function hello() {console.log('world')};
class Foo {
say() { console.log('bar') }
}
let foo = new Foo();
foo.say();
组件通常是 classes class Nav extends React.PureComponent
,因此逻辑连接是对包含 class 的文件进行类似命名,从而导致匹配大小写导入语句 import Nav from './Nav
您可能还有一个实用程序文件,它导出一个函数,而不是 class。同样,有匹配的案例很好 import hello from './hello'
因此,您可能会发现一个常见的结构,例如
src
- App.js
- components/
- Nav.js
- util/
- hello.js
没有官方指南。
大多数项目对 React 组件采用 PascalCase 的原因是模仿该文件的主要导出。
React 组件按照惯例是 PascalCased,当使用 jsx 时,pascal 大小写成为强制性的(实际上只有首字母大写成为强制性的)。
其余文件的 cameCase 或 kebab-case 只是遵循一般 javascript 项目更常见的偏好。
React 没有官方的风格指南。但是您可以使用 AirBnb 最流行的 eslint
React 配置。
在此处阅读更多内容https://github.com/airbnb/javascript/tree/master/react
只是为了补充我的两分钱。正如其他人所说,文件结构是没有意见的。但是,组件命名不是。它们 应该 是 PascalCase
以便 React 知道您是否使用 function
、class
或 HTMLelement
†。
例如:
class input extends Component {...}
不好!为什么?因为 React 不知道你是在尝试使用 input
元素还是 class-based 组件。
这就是您会看到 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
相关的所有内容都在此文件夹中 self-contained。
因此,我可以将它交给某人,他们可以将其放入他们的应用程序中并使用它。
- 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
相关的所有内容都在此文件夹中 self-contained。
因此,我可以将它交给某人,他们可以将其放入他们的应用程序中并使用它。
- 依赖于主文件夹,它描述了组件是
function
还是class
。
- 当发生编译错误时,我确切地知道是哪个文件导致了错误。
缺点:
- 你会有很多小文件夹。
- 有时组件可以从有状态变为无状态(反之亦然),因此如果您严格遵守此命名模式,则必须更新主文件夹以反映更改,这意味着您还需要更新使用此组件的任何其他文件的路径。
- 导入看起来有点冗余和冗长:
import Input from 'components/input/input.js';
其他一般准则:
- 避免默认导出匿名函数:
默认导出的匿名函数示例:
export default () => (
<p>Anonymous Function</p>
);
为什么?因为在测试的时候,这个函数在酶中会显示为:
<_default />
当一个组件中有多个匿名函数时,哪个是哪个!?
<_default />
<_default />
<_default />
- 避免冗长的文件(150 行或更少),因为它会成为 read/understand 的痛苦,调试起来更痛苦。
我经常发现 大多数 组件在适当优化后会下降到 100 行左右。最坏的情况是我必须创建小的子组件来补充主要组件。但!更容易阅读和调试。
什么更容易阅读:
Example #1(34 行补充子组件)
Example #2(全部318行)
示例 #1 模仿读书。多个页面粘在一起时会产生 easy-to-read 体验。与示例 #2 相比,它看起来像一个 mile-long 卷轴,很容易迷路!
- 样式表可以是 snake-case 或驼峰式。
这一项可能令人困惑,但这完全取决于您应用样式的方式。如果您只是像这样导入样式:
import "./styles.css";
那么你可以使用snake-case:
<input className="snake-case" type="text" value="" onChange={this.handleChange} />
但是,如果您使用 css modules
,则需要使用驼峰式:
import { camelCaseClassName } from "./styles.css";
为什么?因为捆绑器(如 Webpack)不支持 snake-case 导入:
<input className={camelCaseClassName} type="text" value="" onChange={this.handleChange} />
结论:创建文件夹结构的方法有很多,但有一些提示和技巧可以保持逻辑文件结构.只需选择最适合您并且不会干扰您旁边工作的人!
换句话说,K.I.S.S === "Keep it simple, silly!"
我正在与我的团队建立一个 React 项目,该项目将使用 mobX 作为状态管理器,外加 TypeScript。
我在 React 项目中看到了一种常见的大小写和命名模式:
- 非 React 文件夹和文件:
camelCase
或kebab-case
- React(在
components
文件夹内):PascalCase
folder/file React 中是否有正式的命名约定?如果没有,是否有此模式所基于的风格指南?或者为什么这个被用得最多的原因?
目前我有一个以 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
索引是我的主要商店 Class,它扩展了我的操作 class。 (我发现一个具有 observable
、computed
和 action
属性的 mobx class 有点混乱)。
Store 文件夹本身有一个 index.js
它导入所有同级商店模块以便稍后将它们组合成一个商店对象(我的项目需要)
Store/
StoreModule/
|---actions.js
|---index.js
AnotherStoreModule/
|---actions.js
|---index.js
index.js
我想没有真正正确的方法,因为它取决于偏好,我觉得上面的方法是可读的,并且在 VSCode 上使用工具查找文件时,它可以更容易地搜索 "i want to see all files that are constants files" 搜索 constants.[component name]
PascalCase 在它们的 classes 中很常见,并且具有驼峰式函数和变量名。一个 JS 例子,
function hello() {console.log('world')};
class Foo {
say() { console.log('bar') }
}
let foo = new Foo();
foo.say();
组件通常是 classes class Nav extends React.PureComponent
,因此逻辑连接是对包含 class 的文件进行类似命名,从而导致匹配大小写导入语句 import Nav from './Nav
您可能还有一个实用程序文件,它导出一个函数,而不是 class。同样,有匹配的案例很好 import hello from './hello'
因此,您可能会发现一个常见的结构,例如
src
- App.js
- components/
- Nav.js
- util/
- hello.js
没有官方指南。 大多数项目对 React 组件采用 PascalCase 的原因是模仿该文件的主要导出。 React 组件按照惯例是 PascalCased,当使用 jsx 时,pascal 大小写成为强制性的(实际上只有首字母大写成为强制性的)。 其余文件的 cameCase 或 kebab-case 只是遵循一般 javascript 项目更常见的偏好。
React 没有官方的风格指南。但是您可以使用 AirBnb 最流行的 eslint
React 配置。
在此处阅读更多内容https://github.com/airbnb/javascript/tree/master/react
只是为了补充我的两分钱。正如其他人所说,文件结构是没有意见的。但是,组件命名不是。它们 应该 是 PascalCase
以便 React 知道您是否使用 function
、class
或 HTMLelement
†。
例如:
class input extends Component {...}
不好!为什么?因为 React 不知道你是在尝试使用 input
元素还是 class-based 组件。
这就是您会看到 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
相关的所有内容都在此文件夹中 self-contained。 因此,我可以将它交给某人,他们可以将其放入他们的应用程序中并使用它。 - 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
相关的所有内容都在此文件夹中 self-contained。 因此,我可以将它交给某人,他们可以将其放入他们的应用程序中并使用它。 - 依赖于主文件夹,它描述了组件是
function
还是class
。 - 当发生编译错误时,我确切地知道是哪个文件导致了错误。
缺点:
- 你会有很多小文件夹。
- 有时组件可以从有状态变为无状态(反之亦然),因此如果您严格遵守此命名模式,则必须更新主文件夹以反映更改,这意味着您还需要更新使用此组件的任何其他文件的路径。
- 导入看起来有点冗余和冗长:
import Input from 'components/input/input.js';
其他一般准则:
- 避免默认导出匿名函数:
默认导出的匿名函数示例:
export default () => (
<p>Anonymous Function</p>
);
为什么?因为在测试的时候,这个函数在酶中会显示为:
<_default />
当一个组件中有多个匿名函数时,哪个是哪个!?
<_default />
<_default />
<_default />
- 避免冗长的文件(150 行或更少),因为它会成为 read/understand 的痛苦,调试起来更痛苦。
我经常发现 大多数 组件在适当优化后会下降到 100 行左右。最坏的情况是我必须创建小的子组件来补充主要组件。但!更容易阅读和调试。
什么更容易阅读:
Example #1(34 行补充子组件)
Example #2(全部318行)
示例 #1 模仿读书。多个页面粘在一起时会产生 easy-to-read 体验。与示例 #2 相比,它看起来像一个 mile-long 卷轴,很容易迷路!
- 样式表可以是 snake-case 或驼峰式。
这一项可能令人困惑,但这完全取决于您应用样式的方式。如果您只是像这样导入样式:
import "./styles.css";
那么你可以使用snake-case:
<input className="snake-case" type="text" value="" onChange={this.handleChange} />
但是,如果您使用 css modules
,则需要使用驼峰式:
import { camelCaseClassName } from "./styles.css";
为什么?因为捆绑器(如 Webpack)不支持 snake-case 导入:
<input className={camelCaseClassName} type="text" value="" onChange={this.handleChange} />
结论:创建文件夹结构的方法有很多,但有一些提示和技巧可以保持逻辑文件结构.只需选择最适合您并且不会干扰您旁边工作的人!
换句话说,K.I.S.S === "Keep it simple, silly!"