一份比较合理的React和JSX的编码规范
- 基本规则
- Class vs
React.createClassvs stateless - Mixins
- 命名
- 组件声明
- 代码对齐
- 引号的使用
- 空格
- 属性
- Refs
- 括号
- 标签
- 函数/方法
- 顺序组织
isMounted
- 一个文件只允许有一个React Component.
- 但是,允许有多个 Stateless,或者 Pure, Components,eslint:
react/no-multi-comp.
- 但是,允许有多个 Stateless,或者 Pure, Components,eslint:
- 总是使用JSX 语法.
- 不要使用
React.createElement除非你要从一个非JSX文件入口启动.
-
如果组件包含内部状态或者refs,使用
class extends React.Component代替React.createClass. eslint:react/prefer-es6-classreact/prefer-stateless-function// 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>; } }
如果组件没有状态或者refs,选择普通的声明式的function定义(不要用箭头函数)来代替class定义:
// bad class Listing extends React.Component { render() { return <div>{this.props.hello}</div>; } } // bad (不鼓励这种非直观的函数声明方式) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div>; }
为什么? Mixins引入了隐式的依赖,会导致命名冲突, 和滚雪球式的复杂度增长.大多数mixins的使用场景都可以更优雅地实现:比如:组件化,高阶组件,工具模块等
-
扩展名: 为React components使用
.jsx扩展名. -
文件名: 使用 PascalCase命名方式. 比如,
ReservationCard.jsx. -
引用名: 使用 PascalCase命名方式为组件命名,使用小驼峰命名方式为组件实例命名。eslint:
react/jsx-pascal-case// bad import reservationCard from './ReservationCard'; // good import ReservationCard from './ReservationCard'; // bad const ReservationItem = <ReservationCard />; // good const reservationItem = <ReservationCard />;
-
组件命名: 组件名称和文件名称保持一致. 例如,
ReservationCard.jsx应该暴露名为ReservationCard的组件引用. 但是,对于某个目录的根组件,使用index.jsx作为文件名并且使用目录名作为组件名:// bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer';
-
高阶组件命名: 使用高阶组件名和传入组件名的组合来作为新生成组件的
displayName属性. 例如, 高阶函数withFoo(), 当传递了组件Bar作为参数时,应该生成一个displayName属性值为withFoo(Bar)的新组件.
为什么? 组件的
displayName可能会被开发者工具或在错误消息中使用,并且displayName的值可以清楚表达这种关系,这有助于人们理解发生了什么。
// 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 命名: 避免使用DOM组件的属性名作为其他用途
为什么? 人们期望像“style”和“className”这样的属性应该有特定的含义。 为应用程序的一个子集更改此API会使代码的可读性和可维护性降低,并可能导致错误。.
// bad
<MyComponent style="fancy" />
// good
<MyComponent variant="fancy" />-
不要使用
displayName作为命名式组件的名字. 使用引用名字来替代.// bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }
-
使用下面的JSX对齐写法. eslint:
react/jsx-closing-bracket-location// bad <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // good <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // 如果props适合写在一行,就保证只有一行 <Foo bar="bar" /> // 子组件保持正常的缩进 <Foo superLongParam="bar" anotherSuperLongParam="baz" > <Quux /> </Foo>
- 为JSX属性值使用双引号 (
"), 为其他JS使用单引号 ('). eslint:jsx-quotes
为什么? 常规HTML属性通常使用双引号而不是单引号,因此JSX属性遵从这个约定。
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />-
为单独闭合的标签使用一个空格分隔. eslint:
no-multi-spaces,react/jsx-space-before-closing// bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />
-
JSX花括号内不要使用空格. eslint:
react/jsx-curly-spacing// bad <Foo bar={ baz } /> // good <Foo bar={baz} />
-
为Props属性名使用小驼峰的命名规则.
// bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} />
-
隐藏值为true的属性值. eslint:
react/jsx-boolean-value// bad <Foo hidden={true} /> // good <Foo hidden />
-
总是为
<img>标签加上alt属性值. 如果图像是可展现的,alt可以是个字符串或者<img>标签必须有role="presentation"这样的属性. eslint:jsx-a11y/img-has-alt// 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" />
-
在
<img>标签的alt属性值中不要使用诸如 "image", "photo", or "picture" 这样的字眼. eslint:jsx-a11y/img-redundant-alt
为什么?
img标签本身已经代表了图像,再用这些词未免画蛇添足了.
// bad
<img src="hello.jpg" alt="Picture of me waving hello" />
// good
<img src="hello.jpg" alt="Me waving hello" />-
使用有效的、非抽象的 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" />
-
在元素上不要使用
accessKey属性. eslint:jsx-a11y/no-access-key
为什么? 键盘快捷键和键盘命令在键盘使用者和屏幕阅读器的不一致性使得可访问性变得更加复杂.
// bad
<div accessKey="h" />
// good
<div />- 避免使用数组下表作为key值,使用一个唯一的值. (why?)
// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}- 始终显示地定义非必须的props属性
为什么? propTypes 是一种文档形式, 提供 defaultProps意味着代码阅读者不必假设更多的可能性.此外,这也意味着你的代码可以省略某些类型的代码检查
// 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 }) {
return <div>{foo}{bar}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: '',
children: null,
};-
总是使用refs callbacks. eslint:
react/no-string-refs// bad <Foo ref="myRef" /> // good <Foo ref={(ref) => { this.myRef = ref; }} />
-
如果JSX超过一行,请使用括号包裹. eslint:
react/jsx-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" />
-
使用箭头函数来替代本地变量.
function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); }
-
render中需要用到的事件响应函数,请在constructor中执行作用域绑定. eslint:
react/jsx-no-bind
为什么? 在render方法中绑定,会在每一次render时产生一个新的function.
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv.bind(this)} />
}
}
// good
class extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv} />
}
}- 不要使用下划线
_前缀来表示React Component的内部方法
为什么? 下划线前缀有时用作其他语言的惯例来表示私有。 但是,与之不同的是,JavaScript中没有原生支持私有,一切都是公开的。 无论您的意图如何,为您的属性添加下划线前缀实际上不会将它们设置为私有,任何属性(下划线前缀或不带前缀)都应被视为公开。 有关详情,请参阅问题[#1024](https://github.com/airbnb/javascript/issues/1024)和[#490](https://github.com/airbnb/javascript/issues/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// bad render() { (<div />); } // good render() { return (<div />); }
class extends React.Component的顺序:
- 可选的
staticmethods constructorgetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- 诸如
onClickSubmit()或者onChangeDescription()的点击或者事件处理函数 - 诸如
getSelectReason()或者getFooterContent()的*rendergetter 方法* - 可选的其他渲染方法 like
renderNavigation()orrenderProfilePicture() render
-
如何定义
propTypes,defaultProps,contextTypes, etc...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的排序: eslint:react/sort-comp
displayNamepropTypescontextTypeschildContextTypesmixinsstaticsdefaultPropsgetDefaultPropsgetInitialStategetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- clickHandlers or eventHandlers like
onClickSubmit()oronChangeDescription() - getter methods for
renderlikegetSelectReason()orgetFooterContent() - optional render methods like
renderNavigation()orrenderProfilePicture() render
- 不要使用
isMounted. eslint:react/no-is-mounted
为什么?
isMounted是一种反模式, 在使用 ES6 classes写法时并不被支持,, 并且正在被官方放弃.
This JSX/React style guide is also available in other languages:
Polish: pietraszekl/javascript
Korean: apple77y/javascript
Portuguese: ronal2do/javascript
Japanese: mitsuruog/javascript-style-guide
Español: agrcrobles/javascript
Ukrainian: ivanzusko/javascript