React 编程规范(by Airbnb)
- 每个文件只包含一个 React 组件
- 不过可以包含多个 Stateless 或 Pure 组件。 eslint 规则:react/no-multi-comp
- 使用
JSX语法 - 除非是从一个非
JSX文件中初始化 app,否则不要使用React.createElement
Class vs React.createClass
- 如果需要管理内部状态或
refs,优先使用class extends React.Component。eslint 规则:react/prefer-es6-class, react/prefer-stateless-function
// badconstListing=React.createClass({render(){return<div/>;}});// goodclassListingextendsReact.Component{render(){return<div/>;}}反之, 则优先使用普通函数(不建议使用箭头函数)。
// badclassListingextendsReact.Component{render(){return<div>{this.props.hello}</div>;}}// bad constListing=({ hello })=>(<div>{hello}</div>);// goodfunctionListing({ hello }){return<div>{hello}</div>;}为什么? Mixins 会增加隐式的依赖,导致命名冲突,并且会以雪球式增加复杂度。在大多数情况下Mixins可以被更好的方法替代,如:组件化,高阶组件,工具模块等。
- 扩展名: 使用
jsx作为 React 组件的扩展名 - 文件名: 文件命名采用帕斯卡命名法,如:
ReservationCard.jsx - 引用名: 组件引用采用帕斯卡命名法,其实例采用驼峰式命名法。eslint rules: react/jsx-pascal-case
// bad
const reservationCard = require('./ReservationCard');
// good
const ReservationCard = require('./ReservationCard');
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
- 组件命名: 使用文件名作为组件名。例如:
ReservationCard.jsx组件的引用名应该是ReservationCard。然而,对于一个目录的根组件,应该使用index.jsx作为文件名,使用目录名作为组件名。
// bad
const Footer = require('./Footer/Footer.jsx')
// bad
const Footer = require('./Footer/index.jsx')
// good
const Footer = require('./Footer')
- 高阶组件命名: 如果是新生成的模块,其模块名的
displayName应该为高阶模块名和传入模块名的组合. 例如, 高阶模块withFoo(), 当传入一个Bar模块的时候, 新的模块名displayName应该为 withFoo(Bar).
为什么?一个模块的
displayName可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好的Debug.
// badexportdefaultfunctionwithFoo(WrappedComponent){returnfunctionWithFoo(props){return<WrappedComponent{...props}foo/>;}}// goodexportdefaultfunctionwithFoo(WrappedComponent){functionWithFoo(props){return<WrappedComponent{...props}foo/>;}constwrappedComponentName=WrappedComponent.displayName||WrappedComponent.name||'Component';WithFoo.displayName=`withFoo(${wrappedComponentName})`;returnWithFoo;}- 属性命名: 避免使用 DOM 属性为组件的属性命名.
为什么?对于
style和className这样的属性名会默认代表一些含义,在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起bug。
// bad<MyComponentstyle="fancy"/>// bad<MyComponentclassName="fancy"/>// good<MyComponentvariant="fancy"/>- 不要通过
displayName来命名组件,通过引用来命名组件
// badexportdefaultReact.createClass({displayName: 'ReservationCard',// stuff goes here});// goodexportdefaultclassReservationCardextendsReact.Component{}- 对于
JSX语法,遵循下面的对齐风格。eslint rules: react/jsx-closing-bracket-location, react/jsx-closing-tag-location
// bad<FoosuperLongParam="bar"anotherSuperLongParam="baz"/>// good<FoosuperLongParam="bar"anotherSuperLongParam="baz"/>// if props fit in one line then keep it on the same line<Foobar="bar"/>// children get indented normally<FoosuperLongParam="bar"anotherSuperLongParam="baz"><Spazz/></Foo>- 对于
JSX使用双引号,对其它所有 JS 属性使用单引号。eslint:jsx-quotes
为什么?因为 JSX 属性不能包含被转移的引号,并且双引号使得如
"don't"一样的连接词很容易被输入。常规的 HTML 属性也应该使用双引号而不是单引号,JSX 属性反映了这个约定。
// bad<Foobar='bar'/>// good<Foobar="bar"/>// bad<Foostyle={{left: "20px"}}/>// good<Foostyle={{left: '20px'}}/>- 在自闭和标签之前留一个空格。eslint: no-multi-spaces, react/jsx-tag-spacing
// bad<Foo/>// very bad<Foo/>// bad<Foo/>// good<Foo/>- 不要在JSX
{}引用括号里两边加空格. eslint: react/jsx-curly-spacing
// bad<Foobar={baz}/>// good<Foobar={baz}/>- 属性名采用驼峰式命名法
// bad<FooUserName="hello"phone_number={12345678}/>// good<FoouserName="hello"phoneNumber={12345678}/>- 属性值为
true,可以忽略赋值。eslint: react/jsx-boolean-value
// bad<Foohidden={true}/>// good<Foohidden/>// good<Foohidden/>img标签要添加alt属性或者role="presentation"。 eslint: jsx-a11y/alt-text
// bad<imgsrc="hello.jpg"/>// good<imgsrc="hello.jpg"alt="Me waving hello"/>// good<imgsrc="hello.jpg"alt=""/>// good<imgsrc="hello.jpg"role="presentation"/>- 不要在
img标签的alt属性中使用 "image", "photo", 或 "picture" 一类的单词。eslint:jsx-a11y/img-redundant-alt
为什么? 屏幕助读器已经将 img 作为图片了,所以没必要再在 alt 属性中进行说明
// bad<imgsrc="hello.jpg"alt="Picture of me waving hello"/>// good<imgsrc="hello.jpg"alt="Me waving hello"/>- 只是用正确且具体的 ARIA 属性。eslint:jsx-a11y/aria-role
// bad - not an ARIA role<divrole="datepicker"/>// bad - abstract ARIA role<divrole="range"/>// good<divrole="button"/>- 不要在元素上使用
accessKey属性。eslint:jsx-a11y/no-access-key
为什么?屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂。
// bad<divaccessKey="h"/>// good<div/>- 避免使用数组的
index来作为属性key的值,推荐使用唯一ID(为什么?)
// bad{todos.map((todo,index)=><Todo{...todo}key={index}/>)}// good{todos.map(todo=>(<Todo{...todo}key={todo.id}/>))}- 对于组件所有的非必要属性需在
defaultProps中定义。
为什么?propTypes 也是一种文档形式,提供 defaultProps 定义更有利于其他人阅读你的代码,并且能省略一些类型检查
// badfunctionSFC({ foo, bar, children }){return<div>{foo}{bar}{children}</div>;}SFC.propTypes={foo: PropTypes.number.isRequired,bar: PropTypes.string,children: PropTypes.node,};// goodfunctionSFC({ 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,};- 尽量少用扩展运算符
为什么?除非你很想传递一些不必要的属性。对于React v15.6.1和更早的版本,你可以给DOM传递一些无效的HTML属性
例外情况:
- 使用了变量提升的高阶组件
functionHOC(WrappedComponent){returnclassProxyextendsReact.Component{Proxy.propTypes={text: PropTypes.string,isLoading: PropTypes.bool};render(){return<WrappedComponent{...this.props}/>}}}- 很清楚扩展运算符是用于对象时。在使用 Mocha 测试组件的时扩展运算符就非常有用
exportdefaultfunctionFoo{constprops={text: '',isPublished: false}return(<div{...props}/>);}注意:使用时要尽可能过滤不必要的属性。使用 prop-types-exact能预防 bug。
//goodrender(){const{ irrelevantProp, ...relevantProps}=this.props;return<WrappedComponent{...relevantProps}/>}//badrender(){const{ irrelevantProp, ...relevantProps}=this.props;return<WrappedComponent{...this.props}/>}- 使用 ref callbacks。eslint:react/no-string-refs
// bad<Fooref="myRef"/>// good<Fooref={(ref)=>{this.myRef=ref;}}/>- 当组件跨行时,要用括号包裹 JSX 标签。eslint: react/wrap-multilines
/// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
// good, when single line
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
- 没有子组件的父组件使用自闭和标签。eslint: react/self-closing-comp
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />
- 如果组件有多行属性,闭合标签应写在新的一行上。eslint: react/jsx-closing-bracket-location
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>
- 使用箭头函数来访问本地变量
functionItemList(props){return(<ul>{props.items.map((item,index)=>(<Itemkey={item.key}onClick={()=>doSomethingWith(item.name,index)}/>))}</ul>);}- 在构造函数中绑定需要在
render方法使用的事件处理函数(绑定this)。eslint:react/jsx-no-bind
为什么?在组件每次
render时, 每次 bind 调用都会创建新的函数
// badclassextendsReact.Component{onClickDiv(){// do stuff}render(){return<divonClick={this.onClickDiv.bind(this)}/>;}}// goodclassextendsReact.Component{constructor(props){super(props);this.onClickDiv=this.onClickDiv.bind(this);}onClickDiv(){// do stuff}render(){return<divonClick={this.onClickDiv}/>;}}- 不要对 React 组件的内置方法使用
underscore前缀
为什么?
_前缀在某些语言中通常被用来表示私有变量或者函数,但在原生 JavaScript 不支持所谓的私有变量,所有的变量函数都是共有的。在变量名之前加上下划线并不会使这些变量私有化,并且所有的属性都应该被视为是共有的。相关 issue:#1024 / #490 。
// bad
React.createClass({
_onClickSubmit() {
// do stuff
}
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
});
- 确保
render方法中有返回值。eslint: react/require-render-return
// badrender(){(<div/>);}// goodrender(){return(<div/>);}- 继承 React.Component 的类的方法遵循下面的顺序
- constructor
- optional static methods
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
- getter methods for render like getSelectReason() or getFooterContent()
- Optional render methods like renderNavigation() or renderProfilePicture()
- render
- 怎么定义 propTypes,defaultProps,contextTypes 等等...
import React, { PropTypes } from 'react';
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;
- 使用 React.createClass 时,方法顺序如下:
- displayName
- propTypes
- contextTypes
- childContextTypes
- mixins
- statics
- defaultProps
- getDefaultProps
- getInitialState
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
- getter methods for render like getSelectReason() or getFooterContent()
- Optional render methods like renderNavigation() or renderProfilePicture()
- render
eslint: react/sort-comp
- 不要再使用
isMounted. eslint: react/no-is-mounted
为什么?isMounted 是一种反模式,在 ES6 classes 中不可用。官方未来将会删除改属性。·
·