算是最合理的React/JSX编码规范之一了
此编码规范主要基于目前流行的JavaScript标准,尽管某些其他约定(如async/await,静态class属性)可能在不同的项目中被引入或者被禁用。目前的状态是任何stage-3之前的规范都不包括也不推荐使用。
内容目录
- 基本规范
- Class vs React.createClass vs stateless
- Mixins
- 命名
- 声明模块
- 代码对齐
- 单引号还是双引号
- 空格
- 属性
- Refs引用
- 括号
- 标签
- 函数/方法
- 模块生命周期
- isMounted
Basic Rules 基本规范
每个文件只写一个模块.
- 但是多个无状态模块可以放在单个文件中. eslint:
react/no-multi-comp.
- 但是多个无状态模块可以放在单个文件中. eslint:
- 推荐使用JSX语法.
- 不要使用
React.createElement,除非从一个非JSX的文件中初始化你的app.
创建模块
Class vs React.createClass vs stateless
- 如果你的模块有内部状态或者是
refs, 推荐使用class extends React.Component而不是React.createClass.
eslint:react/prefer-es6-classreact/prefer-stateless-function```jsx // bad const Listing = React.createClass({ // … render() { return{this.state.hello}; } });
// good class Listing extends React.Component { // … render() { return
<br />如果你的模块没有状态或是没有引用`refs`, 推荐使用普通函数(非箭头函数)而不是类:```jsx// badclass 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>);// goodfunction Listing({ hello }) {return <div>{hello}</div>;}
Mixins
为什么? Mixins 会增加隐式的依赖,导致命名冲突,并且会以雪球式增加复杂度。在大多数情况下Mixins可以被更好的方法替代,如:组件化,高阶组件,工具模块等。
Naming 命名
扩展名: React模块使用
.jsx扩展名.文件名: 文件名使用帕斯卡命名. 如,
ReservationCard.jsx.引用命名: React模块名使用帕斯卡命名,实例使用骆驼式命名. eslint:
react/jsx-pascal-case```jsx // bad import reservationCard from ‘./ReservationCard’;
// good import ReservationCard from ‘./ReservationCard’;
// bad
const ReservationItem =
// good
const reservationItem =
-**模块命名**: 模块使用当前文件名一样的名称. 比如 `ReservationCard.jsx` 应该包含名为 `ReservationCard`的模块. 但是,如果整个文件夹是一个模块,使用 `index.js`作为入口文件,然后直接使用 `index.js` 或者文件夹名作为模块的名称:```jsx// badimport Footer from './Footer/Footer';// badimport Footer from './Footer/index';// goodimport Footer from './Footer';
- 高阶模块命名: 对于生成一个新的模块,其中的模块名
displayName应该为高阶模块名和传入模块名的组合. 例如, 高阶模块withFoo(), 当传入一个Bar模块的时候, 生成的模块名displayName应该为withFoo(Bar).为什么?一个模块的
displayName可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好的Debug.
// badexport default function withFoo(WrappedComponent) {return function WithFoo(props) {return <WrappedComponent {...props} foo />;}}// goodexport default function withFoo(WrappedComponent) {function WithFoo(props) {return <WrappedComponent {...props} foo />;}const wrappedComponentName = WrappedComponent.displayName|| WrappedComponent.name|| 'Component';WithFoo.displayName = `withFoo(${wrappedComponentName})`;return WithFoo;}
- 属性命名: 避免使用DOM相关的属性来用作其他的用途。
为什么?对于
style和className这样的属性名,我们都会默认它们代表一些特殊的含义,如元素的样式,CSS class的名称。在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起bug。
// bad<MyComponent style="fancy" />// good<MyComponent variant="fancy" />
Declaration 声明模块
- 不要使用
displayName来命名React模块,而是使用引用来命名模块, 如 class 名称. ```jsx // bad export default React.createClass({ displayName: ‘ReservationCard’, // stuff goes here });
// good export default class ReservationCard extends React.Component { }
<a name="fa61f477"></a>## Alignment 代码对齐-遵循以下的JSX语法缩进/格式. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) [`react/jsx-closing-tag-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md)```jsx// bad<Foo superLongParam="bar"anotherSuperLongParam="baz" />// good, 有多行属性的话, 新建一行关闭标签<FoosuperLongParam="bar"anotherSuperLongParam="baz"/>// 若能在一行中显示, 直接写成一行<Foo bar="bar" />// 子元素按照常规方式缩进<FoosuperLongParam="bar"anotherSuperLongParam="baz"><Quux /></Foo>
Quotes 单引号还是双引号
- 对于JSX属性值总是使用双引号(
"), 其他均使用单引号('). eslint:jsx-quotes为什么? HTML属性也是用双引号, 因此JSX的属性也遵循此约定.
// bad<Foo bar='bar' />// good<Foo bar="bar" />// bad<Foo style={{ left: "20px" }} />// good<Foo style={{ left: '20px' }} />
Spacing 空格
- 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行. eslint:
no-multi-spaces,react/jsx-tag-spacing```jsx // bad
// very bad
-
不要在JSX `{}` 引用括号里两边加空格. eslint: [`react/jsx-curly-spacing`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md)jsx
// bad
<a name="5ae647b8"></a>
## Props 属性
-
JSX属性名使用骆驼式风格`camelCase`.jsx
// bad
-
如果属性值为 `true`, 可以直接省略. eslint: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md)jsx
// bad
-
`<img>` 标签总是添加 `alt` 属性. 如果图片以presentation(感觉是以类似PPT方式显示?)方式显示,`alt` 可为空, 或者`<img>` 要包含`role="presentation"`. eslint: [`jsx-a11y/alt-text`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md)jsx
// bad
// good
// good
// good
-
不要在 `alt` 值里使用如 "image", "photo", or "picture"包括图片含义这样的词, 中文也一样. eslint: [`jsx-a11y/img-redundant-alt`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/img-redundant-alt.md)
> 为什么? 屏幕助读器已经把 `img` 标签标注为图片了, 所以没有必要再在 `alt` 里说明了.jsx
// bad
// good
-
使用有效正确的 aria `role`属性值 [ARIA roles](https://www.w3.org/TR/wai-aria/roles#usage_intro). eslint: [`jsx-a11y/aria-role`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-role.md)jsx
// bad - not an ARIA role
// bad - abstract ARIA role
// good
-
不要在标签上使用 `accessKey` 属性. eslint: [`jsx-a11y/no-access-key`](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-access-key.md)
> 为什么? 屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂.jsx
// bad
// good
- 避免使用数组的index来作为属性`key`的值,推荐使用唯一ID. ([为什么?](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318))jsx
// bad
{todos.map((todo, index) =>
- 对于所有非必须的属性,总是手动去定义`defaultProps`属性.
> 为什么? propTypes 可以作为模块的文档说明, 并且声明 defaultProps 的话意味着阅读代码的人不需要去假设一些默认值。更重要的是, 显示的声明默认属性可以让你的模块跳过属性类型的检查.jsx
// bad
function SFC({ foo, bar, children }) {
return - 尽可能少地使用扩展运算符
> 为什么? 除非你很想传递一些不必要的属性。对于React v15.6.1和更早的版本,你可以[给DOM传递一些无效的HTML属性](https://doc.react-china.org/blog/2017/09/08/dom-attributes-in-react-16.html)
例外情况:
- 使用了变量提升的高阶组件jsx
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return - 只有在清楚明白扩展对象时才使用扩展运算符。这非常有用尤其是在使用Mocha测试组件的时候。jsx
export default function Foo {
const props = {
text: ‘’,
isPublished: false
}
return ();
}
特别提醒:尽可能地筛选出不必要的属性。同时,使用[prop-types-exact](https://www.npmjs.com/package/prop-types-exact)来预防问题出现。jsx
// good
render() {
const { irrelevantProp, …relevantProps } = this.props;
return <a name="Refs"></a>
## Refs
-
总是在Refs里使用回调函数. eslint: [`react/no-string-refs`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md)jsx
// bad
<a name="be7e5abd"></a>
## Parentheses 括号
-
将多行的JSX标签写在 `()`里. eslint: [`react/jsx-wrap-multilines`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md)jsx
// bad
render() {
return <a name="98d29710"></a>
## Tags 标签
-
对于没有子元素的标签来说总是自己关闭标签. eslint: [`react/self-closing-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md)jsx
// bad
// good
-如果模块有多行的属性, 关闭标签时新建一行. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md)```jsx// bad<Foobar="bar"baz="baz" />// good<Foobar="bar"baz="baz"/>
Methods 函数
- 使用箭头函数来获取本地变量.
function ItemList(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都会新建一个新的函数,浪费资源.
// badclass extends React.Component {onClickDiv() {// do stuff}render() {return <div onClick={this.onClickDiv.bind(this)} />;}}// goodclass extends React.Component {constructor(props) {super(props);this.onClickDiv = this.onClickDiv.bind(this);}onClickDiv() {// do stuff}render() {return <div onClick={this.onClickDiv} />;}}
- 在React模块中,不要给所谓的私有函数添加
_前缀,本质上它并不是私有的.为什么?
_下划线前缀在某些语言中通常被用来表示私有变量或者函数。但是不像其他的一些语言,在JS中没有原生支持所谓的私有变量,所有的变量函数都是共有的。尽管你的意图是使它私有化,在之前加上下划线并不会使这些变量私有化,并且所有的属性(包括有下划线前缀及没有前缀的)都应该被视为是共有的。了解更多详情请查看Issue #1024, 和 #490 。
// badReact.createClass({_onClickSubmit() {// do stuff},// other stuff});// goodclass extends React.Component {onClickSubmit() {// do stuff}// other stuff}
- 在
render方法中总是确保return返回值. eslint:react/require-render-return```jsx // bad render() { (); }
// good render() { return (
); }
<a name="e6fe9da0"></a>## Ordering React 模块生命周期- `class extends React.Component` 的生命周期函数:1. 可选的 `static` 方法2. `constructor` 构造函数3. `getChildContext` 获取子元素内容4. `componentWillMount` 模块渲染前5. `componentDidMount` 模块渲染后6. `componentWillReceiveProps` 模块将接受新的数据7. `shouldComponentUpdate` 判断模块需不需要重新渲染8. `componentWillUpdate` 上面的方法返回 `true`, 模块将重新渲染9. `componentDidUpdate` 模块渲染结束10. `componentWillUnmount` 模块将从DOM中清除, 做一些清理任务11. _点击回调或者事件处理器_ 如 `onClickSubmit()` 或 `onChangeDescription()`12. _`render` 里的 getter 方法_ 如 `getSelectReason()` 或 `getFooterContent()`13. _可选的 render 方法_ 如 `renderNavigation()` 或 `renderProfilePicture()`14. `render` render() 方法-如何定义 `propTypes`, `defaultProps`, `contextTypes`, 等等其他属性...```jsximport 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;
React.createClass的生命周期函数,与使用class稍有不同: eslint:react/sort-comp
displayName设定模块名称propTypes设置属性的类型contextTypes设置上下文类型childContextTypes设置子元素上下文类型mixins添加一些mixinsstaticsdefaultProps设置默认的属性值getDefaultProps获取默认属性值getInitialState或者初始状态getChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- clickHandlers or eventHandlers like
onClickSubmit()oronChangeDescription() - getter methods for
renderlikegetSelectReason()orgetFooterContent() - Optional render methods like
renderNavigation()orrenderProfilePicture() render
isMounted
- 不要再使用
isMounted. eslint:react/no-is-mounted
为什么?
isMounted反人类设计模式:(), 在 ES6 classes 中无法使用, 官方将在未来的版本里删除此方法.
