react 项目结构及编码规范
项目结构
project-name ---------------------------------- 项目名字
build --------------------------------------- 打包后文件夹
config -------------------------------------- webpack配置文件
node-modules -------------------------------- 项目依赖第三方库
scripts ------------------------------------- 打包,运行,测试脚本
src ----------------------------------------- 项目源码文件夹
assets ------------------------------------ 公共样式,字体等文件夹
common ------------------------------------ 定义的工具函数、通用常量、ajax请求等公用组件
components -------------------------------- 公用组件文件夹(提升为通用组件,以数据驱动为主)
css --------------------------------------- 组件自定义样式文件夹
pages ------------------------------------- 完成界面文件夹(组成项目中整个界面)
module1 --------------------------------- 模块一
module2 --------------------------------- 模块二
**.js ------------------------------------- 项目入口文件
test ---------------------------------------- 测试文件夹
.babelrc ------------------------------------ 是babel配置文件
package.json -------------------------------- 项目依赖库文件
编码规范
Table of Contents
- Basic Rules
- Class vs
React.createClass
vs stateless - Mixins
- Naming
- Declaration
- Alignment
- Quotes
- Spacing
- Props
- Refs
- Parentheses
- Tags
- Methods
- Ordering
isMounted
Basic Rules
- Only include one React component per file. 一个文件包含一个 React Component 组件。
- However, multiple Stateless, or Pure, Components are allowed per file. eslint:
react/no-multi-comp
. 特殊情况:可以包含多个纯组件
- However, multiple Stateless, or Pure, Components are allowed per file. eslint:
- Always use JSX syntax. 使用 JSX 语法
- Do not use
React.createElement
unless you’re initializing the app from a file that is not JSX.(不要使用 React.createElement 方法,除非你在初始化APP时的 index.js 中不使用 JSX 语法)
Class vs React.createClass
vs stateless
-
If you have internal state and/or refs, prefer
class extends React.Component
overReact.createClass
. eslint:react/prefer-es6-class
react/prefer-stateless-function
将 React 类放在一个变量中(const Name = React.createClass),还是直接继承自组件(class Name extends React.Component)?如果内部含有state 或者refs 等,最好设置 React.Component. 如果仅仅是单向组件,只有props, 没有复杂用户交互,可以设置 createClass 或者 Purecomponent.
// bad const Listing = React.createClass({ // ... render() { return <div>{this.state.hello}</div>; } }); // good class Listing extends React.Component { // ... render() { return <div>{this.state.hello}</div>; } }
And if you don’t have state or refs, prefer normal functions (not arrow functions) over classes。如果没有 state 和 refs,只有 props 不需要使用箭头函数,直接使用普通函数传值即可。
// bad class Listing extends React.Component { render() { return <div>{this.props.hello}</div>; } } // bad (relying on function name inference is discouraged) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div>; }
Mixins
Why? Mixins introduce implicit dependencies, cause name clashes, and cause snowballing complexity. Most use cases for mixins can be accomplished in better ways via components, higher-order components, or utility modules.
不要使用混合组件:mixins 引入了深层依赖关系,可能名称冲突,并导致滚雪球式复杂性。mixin的大多数用例都可以通过组件、高阶组件、实用程序模块以更好的方式完成。
划重点:权责分明,避免深层引用。一个函数对应一个主要功能,一个组件对应界面的一个 UI 部分。尽量实现组件的继承和复用。
Naming
-
Extensions: Use
.js
extension for React components. -
Filename: Use reservation-card for filenames. E.g.,
reservation-card.js
.文件名使用小写开头(连接符)
// bad import reservationCard from './reservation-card'; // good import ReservationCard from './reservation-card'; // bad const ReservationItem = <ReservationCard />; // good const reservationItem = <ReservationCard />;
-
Component Naming: Use the filename as the component name. For example,
ReservationCard.jsx
should have a reference name ofReservationCard
. However, for root components of a directory, useindex.jsx
as the filename and use the directory name as the component name:React 组件使用大写开头,组件名和文件名相同(便于调用)。变量名使用小写开头(驼峰)
// bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer';
-
Higher-order Component Naming: Use a composite of the higher-order component’s name and the passed-in component’s name as the
displayName
on the generated component. For example, the higher-order componentwithFoo()
, when passed a componentBar
should produce a component with adisplayName
ofwithFoo(Bar)
.Why? A component’s
displayName
may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening.// bad export default function withFoo(WrappedComponent) { return function WithFoo(props) { return <WrappedComponent {...props} foo />; } } // good export default function withFoo(WrappedComponent) { function WithFoo(props) { return <WrappedComponent {...props} foo />; } const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component'; WithFoo.displayName = `withFoo(${wrappedComponentName})`; return WithFoo; }
-
Props Naming: Avoid using DOM component prop names for different purposes.
Why? People expect props like
style
andclassName
to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs.
props不能和已有 HTML 属性重复
// bad
<MyComponent style="fancy" />
// bad
<MyComponent className="fancy" />
// good
<MyComponent variant="fancy" />
Declaration
-
Do not use
displayName
for naming components. Instead, name the component by reference.// bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }
Alignment
-
Follow these alignment styles for JSX syntax. eslint:
react/jsx-closing-bracket-location
react/jsx-closing-tag-location
JSX 对齐规则// bad <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // good 多行分开显示 <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // if props fit in one line then keep it on the same line 一行内显示 <Foo bar="bar" /> // children get indented normally <Foo superLongParam="bar" anotherSuperLongParam="baz" > <Quux /> </Foo> // bad {showButton && <Button /> } // bad { showButton && <Button /> } // good 多行显示JSX {showButton && ( <Button /> )} // good {showButton && <Button />}
Quotes
-
Always use double quotes (
"
) for JSX attributes, but single quotes ('
) for all other JS. eslint:jsx-quotes
Why? Regular HTML attributes also typically use double quotes instead of single, so JSX attributes mirror this convention.
// bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} />
Spacing
-
Always include a single space in your self-closing tag. eslint:
no-multi-spaces
,react/jsx-tag-spacing
// bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />
-
Do not pad JSX curly braces with spaces. eslint:
react/jsx-curly-spacing
// bad <Foo bar={ baz } /> // good <Foo bar={baz} />
Props
-
Always use camelCase for prop names.
// bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} />
-
Omit the value of the prop when it is explicitly
true
. eslint:react/jsx-boolean-value
如果传值是 true 可以省略
// bad <Foo hidden={true} /> // good <Foo hidden /> // good <Foo hidden />
-
Always include an
alt
prop on<img>
tags. If the image is presentational,alt
can be an empty string or the<img>
must haverole="presentation"
. eslint:jsx-a11y/alt-text
// bad <img src="hello.jpg" /> // good <img src="hello.jpg" alt="Me waving hello" /> // good <img src="hello.jpg" alt="" /> // good <img src="hello.jpg" role="presentation" />
-
Do not use words like "image", "photo", or "picture" in
<img>
alt
props. eslint:jsx-a11y/img-redundant-alt
Why? Screenreaders already announce
img
elements as images, so there is no need to include this information in the alt text.// bad <img src="hello.jpg" alt="Picture of me waving hello" /> // good <img src="hello.jpg" alt="Me waving hello" />
-
Use only valid, non-abstract ARIA roles. eslint:
jsx-a11y/aria-role
// bad - not an ARIA role <div role="datepicker" /> // bad - abstract ARIA role <div role="range" /> // good <div role="button" />
-
Do not use
accessKey
on elements. eslint:jsx-a11y/no-access-key
Why? Inconsistencies between keyboard shortcuts and keyboard commands used by people using screenreaders and keyboards complicate accessibility.
// bad
<div accessKey="h" />
// good
<div />
- Avoid using an array index as
key
prop, prefer a stable ID.
Why? Not using a stable ID is an anti-pattern because it can negatively impact performance and cause issues with component state.
We don’t recommend using indexes for keys if the order of items may change.
注意:避免使用箭头函数的 index 作为子组件的 key 值。不使用稳定ID是一种反模式,因为它会对性能产生负面影响,并导致组件状态问题。如果子组件item可能变化是,例如评论,最好使用id不使用index。
// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
- Always define explicit defaultProps for all non-required props.
Why? propTypes are a form of documentation, and providing defaultProps means the reader of your code doesn’t have to assume as much. In addition, it can mean that your code can omit certain type checks.
使用 SFC.defaultProps 可以让使用组件的人知道需要传入的参数和类型;同时写 propTypes 也更好
// bad
function SFC({ foo, bar, children }) {
return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
// good
function SFC({ foo, bar, children }) {
return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: '',
children: null,
};
- Use spread props sparingly. 尽量少用 props 不要传没必要的 props
Why? Otherwise you’re more likely to pass unnecessary props down to components. And for React v15.6.1 and older, you could pass invalid HTML attributes to the DOM.
Exceptions:
- HOCs that proxy down props and hoist propTypes
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return <WrappedComponent {...this.props} />
}
}
}
- Spreading objects with known, explicit props. This can be particularly useful when testing React components with Mocha’s beforeEach construct.
export default function Foo {
const props = {
text: '',
isPublished: false
}
return (<div {...props} />);
}
Notes for use: Filter out unnecessary props when possible. Also, use prop-types-exact to help prevent bugs.
// bad
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...this.props} />
}
// good
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...relevantProps} />
}
Refs
-
Always use ref callbacks. eslint:
react/no-string-refs
使用箭头函数将refs绑定到属性上,避免直接使用字符串。
// bad <Foo ref="myRef" /> // good <Foo ref={(ref) => { this.myRef = ref; }} />
Parentheses
-
Wrap JSX tags in parentheses when they span more than one line. eslint:
react/jsx-wrap-multilines
return 多行加括号,单行不需要加括号
// bad render() { return <MyComponent variant="long body" foo="bar"> <MyChild /> </MyComponent>; } // good render() { return ( <MyComponent variant="long body" foo="bar"> <MyChild /> </MyComponent> ); } // good, when single line render() { const body = <div>hello</div>; return <MyComponent>{body}</MyComponent>; }
Tags
-
Always self-close tags that have no children. eslint:
react/self-closing-comp
// bad <Foo variant="stuff"></Foo> // good <Foo variant="stuff" />
-
If your component has multi-line properties, close its tag on a new line. eslint:
react/jsx-closing-bracket-location
// bad <Foo bar="bar" baz="baz" /> // good <Foo bar="bar" baz="baz" />
Methods
-
Use arrow functions to close over local variables.
function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); }
-
Bind event handlers for the render method in the constructor. eslint:
react/jsx-no-bind
Why? A bind call in the render path creates a brand new function on every single render.
// bad class extends React.Component { onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv.bind(this)} />; } } // good class extends React.Component { onClickDiv = () => { // do stuff } render() { return <div onClick={this.onClickDiv} />; } }
-
Do not use underscore prefix for internal methods of a React component. 组件内部的函数名不要以下划线开头。 > Why? Underscore prefixes are sometimes used as a convention in other languages to denote privacy. But, unlike those languages, there is no native support for privacy in JavaScript, everything is public. Regardless of your intentions, adding underscore prefixes to your properties does not actually make them private, and any property (underscore-prefixed or not) should be treated as being public. See issues #1024, and #490 for a more in-depth discussion.
// bad React.createClass({ _onClickSubmit() { // do stuff }, // other stuff }); // good class extends React.Component { onClickSubmit() { // do stuff } // other stuff }
-
Be sure to return a value in your
render
methods. eslint:react/require-render-return
render 中必须有 return 方法。
// bad render() { (<div />); } // good render() { return (<div />); }
Ordering
-
Ordering for
class extends React.Component
: -
optional
static
methods constructor
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- clickHandlers or eventHandlers like
onClickSubmit()
oronChangeDescription()
- getter methods for
render
likegetSelectReason()
orgetFooterContent()
- optional render methods like
renderNavigation()
orrenderProfilePicture()
-
render
-
How to define
propTypes
,defaultProps
,contextTypes
, etc...生命周期函数+事件处理函数+获取信息函数+渲染函数
import React from 'react'; import PropTypes from 'prop-types'; const propTypes = { id: PropTypes.number.isRequired, url: PropTypes.string.isRequired, text: PropTypes.string, }; const defaultProps = { text: 'Hello World', }; class Link extends React.Component { static methodsAreOk() { return true; } render() { return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>; } } Link.propTypes = propTypes; Link.defaultProps = defaultProps; export default Link;
-
Ordering for
React.createClass
: eslint:react/sort-comp
-
displayName
propTypes
contextTypes
childContextTypes
mixins
statics
defaultProps
getDefaultProps
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- clickHandlers or eventHandlers like
onClickSubmit()
oronChangeDescription()
- getter methods for
render
likegetSelectReason()
orgetFooterContent()
- optional render methods like
renderNavigation()
orrenderProfilePicture()
render
isMounted
- Do not use
isMounted
. eslint:react/no-is-mounted
Why?
isMounted
is an anti-pattern, is not available when using ES6 classes, and is on its way to being officially deprecated.