Open Marinerer opened 6 years ago
简书@艾特老干部
[TOC]
Airbnb React/JSX 编码规范 算是最合理的 React/JSX 编码规范之一了
此编码规范主要基于目前流行的 JavaScript 标准,尽管某些其他约定 (如 async/await,静态 class 属性) 可能在不同的项目中被引入或者被禁用。目前的状态是任何 stage-3 之前的规范都不包括也不推荐使用。
react/no-multi-comp
.React.createElement
,除非从一个非JSX的文件中初始化你的app.官网文档: React Without ES6 、 Typechecking With PropTypes
React 中可以通过三种方式创建组件:ES6 class
、createReactClass
、函数式组件 。
如果你的模块有内部状态或者是
refs
, 推荐使用class extends React.Component
而不是React.createClass
. eslint:react/prefer-es6-class
react/prefer-stateless-function
如果组件有内部状态、引用 refs
,或者使用了生命周期方法,优先使用 class
写法 :
class Greeting extends React.Component {
// ...
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
这种情况下 PropTypes/DefaultProps/contextTypes
使用 ES7 类静态属性提案的写法:
import React from 'react';
import PropTypes from 'prop-types';
class Link extends React.Component {
static propTypes = {
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
size: React.PropTypes.oneOf(['large', 'small']),
text: PropTypes.string,
}
static defaultProps = {
size: 'large',
text: 'Hello World',
}
static methodsAreOk() {
return true;
}
render() {
return (
<a href={this.props.url} data-id={this.props.id}>
{this.props.text}
</a>
);
}
}
export default Link;
⚠️ 提示:
propTypes
及默认值:
propTypes
中声明类型;如果没有内部状态、方法或引用 refs
,使用 Function 写法:
// bad ❌ (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good ✅
function Listing({ hello }) {
return <div>{hello}</div>;
}
这种情况下 PropTypes/DefaultProps/contextTypes
使用 静态属性 的写法:
MyComp.propTypes = {
name: PropTypes.string
};
MyComp.defaultProps = {
name: 'jason'
};
对于简单的初始化,直接使用 ES7 实例属性提案声明写法:
class MyComp extends React.Component {
state = {
foo: 'bar'
};
// ....
}
对于需要计算后才能初始化的 State,使用构造函数声明写法:
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 'bar'
};
}
// ....
}
不要对 this.state
赋值,使用 this.setState()
更新 State:
// bad ❌
this.state.foo = 'bar';
this.forceUpdate();
// good ✅
this.setState({
foo: 'bar'
})
属性命名:
className
代替 class
属性;htmlFor
代替 for
属性;传递给 HTML 的属性:
data-
前缀,React 不会渲染非标准属性;aria-
可以正常使用。<Foo
userName="hello"
phoneNumber={12345678}
/>
true
,可以直接省略 eslint: react/jsx-boolean-value
:// bad ❌
<Foo
hidden={true}
/>
// good ✅
<Foo
hidden
/>
// 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" />
不要在 alt
值里使用如 "image", "photo", or "picture"包括图片含义这样的词, 中文也一样. eslint: jsx-a11y/img-redundant-alt
为什么? 屏幕助读器已经把
img
标签标注为图片了, 所以没有必要再在alt
里说明了.
// bad
<img src="hello.jpg" alt="Picture of me waving hello" />
// good
<img src="hello.jpg" alt="Me waving hello" />
使用有效正确的 aria role
属性值 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
必需,且不要使用 index
作为 key
,推荐使用唯一ID。 (为什么?)function Todos({ todos }) {
return (
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
)
}
defaultProps
属性.为什么? 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, 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属性
例外情况:
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return <WrappedComponent {...this.props} />
}
}
}
export default function Foo {
const props = {
text: '',
isPublished: false
}
return (<div {...props} />);
}
特别提醒:尽可能地筛选出不必要的属性。同时,使用prop-types-exact来预防问题出现。
// good
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...relevantProps} />
}
// bad
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...this.props} />
}
总是在Refs里使用回调函数. eslint: react/no-string-refs
// bad ❌
<Foo
ref="myRef"
/>
// good ✅
<Foo
ref={(ref) => { this.myRef = ref }}
/>
为什么? Mixins 会增加隐式的依赖,导致命名冲突,并且会以雪球式增加复杂度。在大多数情况下Mixins可以被更好的方法替代,如:组件化,高阶组件,工具模块等。
推荐两种写法:
this
)// 箭头函数写法 ✅
// class组件
class MyComp extends Component {
handleClick = () => {
console.log(this);
}
render() {
return <div onClick={this.handleClick}>Please Click!</div>;
}
}
// functional组件
function MyComp(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
</ul>
)
}
// 构造函数中绑定写法 ✅
class MyComp extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
render() {
return <div onClick={this.handleClick}>Please Click!</div>;
}
}
⚠️ 注意:
在
render()
中使用事件处理方法时,提前在constructor
中绑定this
,而不是引用的时候绑定。如果在每次 render 过程中,再调用
bind
都会新建一个新的函数,浪费资源。
// bad ❌
class MyComp extends Component {
handleClick() {
console.log(this)
}
render() {
return <div onClick={this.handleClick.bind(this)}>Please Click!</div>;
}
}
在React模块中,不要给所谓的私有函数添加 _
前缀,本质上它并不是私有的.
为什么?
_
下划线前缀在某些语言中通常被用来表示私有变量或者函数。但是不像其他的一些语言,在JS中没有原生支持所谓的私有变量,所有的变量函数都是共有的。尽管你的意图是使它私有化,在之前加上下划线并不会使这些变量私有化,并且所有的属性(包括有下划线前缀及没有前缀的)都应该被视为是共有的。了解更多详情请查看Issue #1024, 和 #490 。
// bad
React.createClass({
_onClickSubmit() {
// do stuff
},
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
}
在 render
方法中总是确保 return
返回值. eslint: react/require-render-return
// bad
render() {
(<div />);
}
// good
render() {
return (<div />);
}
静态static
属性
静态static
方法
生命周期方法
constructor
:构造函数getChildContext
:获取子元素内容componentWillMount
:组件渲染前componentDidMount
:组件渲染后componentWillReceiveProps
:组件将接收新的数据shouldComponentUpdate
:判断组件是否需要重新渲染componentWillUpdate
:若上面函数返回true
,组件将重新渲染componentDidUpdate
:组件渲染后componentWillUnmount
:组件将被销毁点击回调或者事件处理器
onClickSubmit
、onChangeDescription
render
的 getter方法
getSelectReason
、getFooterContent
可选的render
方法
renderNavigation
、renderProfilePicture
render
方法(必须有返回值)
不要使用 displayName
来命名组件,通过引用来命名。
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
})
// good
class ReservationCard extends React.Component {
// stuff...
}
export default ReservationCard
遵循以下的JSX语法缩进/格式. eslint: react/jsx-closing-bracket-location
react/jsx-closing-tag-location
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good, 有多行属性的话, 新建一行关闭标签
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// 若能在一行中显示, 直接写成一行
<Foo bar="bar" />
// 子元素按照常规方式缩进
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>
.jsx
。PascalCase
帕斯卡命名(如 MyComp.jsx
)。index.js
作为入口文件。react/jsx-pascal-case
camelCase
小驼峰命名(如userName
)displayName
应该为高阶模块名和传入模块名的组合. 例如, 高阶模块 withFoo()
, 当传入一个 Bar
模块的时候, 生成的模块名 displayName
应该为 withFoo(Bar)
。引用命名
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
高阶组件命名
为什么?一个模块的
displayName
可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好的理解模块发生了什么,更好的Debug.
/**
* 高阶组件HOC 命名
*/
// 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;
}
属性命名: 避免使用DOM相关的属性来用作其他的用途。
为什么?对于
style
和className
这样的属性名,我们都会默认它们代表一些特殊的含义,如元素的样式,CSS class的名称。在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起bug。
// bad
<MyComponent style="fancy" />
// good
<MyComponent variant="fancy" />
对于没有子元素的标签自关闭,对于有多个属性的使用多行书写,并在新行关闭:
<Foo className="stuff" />
<Foo
bar="bar"
baz="baz"
/>
总是在自闭合的标签/>
前加一个空格。不要在JSX 的 {}
引用括号里两边加空格:
<Foo />
<Foo bar={baz} />
属性值使用双引号,其他都使用单引号。
<Foo bar={baz} foo="bar" />
// good { left: '20px' } 为一个对象
<Foo style={{ left: '20px' }} />
将多行的 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, ✅ 单行可以不需要
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
避免使用数组的索引作为 key
值,优先使用唯一 ID 作为 key 值。 (参考文章)
// bad ❌
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good ✅
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
同时满足以下条件时可以使用数组索引作为 key
值:
.
.
.
.
.
系列博客整理: