解除React一段时间了,作为一个初创型项目,已经上来就开始撸代码,感觉代码写起来非常随意,项目中lint配置也是少之又少。今天看了下Airbnb关于react代码的规范,大概为以下几点。
其实关于react的lint检查也有专门的库, eslint-plugin-react。
1. 每个文件定义一个React component
该规定在eslint-plugin-react中也有约束,即react/no-multi-comp
2. Class vs React.createClass vs stateless
(1) 如果组件中定义了内部状态,或者使用了ref引用,则使用React.createClass来创建组件。对应lint为react/prefer-es6-class
react/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>;
}
}
(2)如果你的组件是一个无状态组件,则最好使用正常函数(非箭头函数)。
// 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>;
}
3. Mixin
不要使用Mixin,官网详细说明了,为什么不让用Mixin。不要使用Mixin
。
4. 命名约束
- 后缀:component使用.jsx后缀
- 文件名:文件名使用帕斯卡命名法,即每个单词的首字母大写。E.g., ReservationCard.jsx.
- 引用命名:对于React component使用帕斯卡命名法,对于实例使用峰驼命名法。
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,但对一个文件夹来说,该组件为root组件,则可以定义一个文件index.jsx,使用文件夹名作为组件名。
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';
- 高阶组件的命名:使用高阶组件名和传入的参数一起生成新的组件名。
// 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自带的Props来命名。
// bad
<MyComponent style="fancy" />
// bad
<MyComponent className="fancy" />
// good
<MyComponent variant="fancy" />
4. Declaration
声明一个component时,不要使用displayName属性来命名该组件,使用引用来命名该组件。
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}
4. 对齐
详见react/jsx-closing-bracket-location
react/jsx-closing-tag-location
// 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>
5. 引号
JSX 属性使用双引号"",JS相关属性使用单引号''
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />
6.空格
总是在自动关闭的标签前加一个空格,正常情况下也不需要换行. eslint: no-multi-spaces
, react/jsx-tag-spacing
// bad
<Foo variant="stuff"></Foo>
// good
<Foo variant="stuff" />
不要在JSX {}
引用括号里两边加空格. eslint: react/jsx-curly-spacing
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />
6.Props
JSX属性名使用骆驼式风格camelCase.
// 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
/>
// good
<Foo hidden />
<img>
标签总是添加 alt
属性. 如果图片以presentation(感觉是以类似PPT方式显示?)方式显示,alt
可为空, 或者<img>
要包含role="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" />
不要在 alt
值里使用如 "image", "photo", or "picture"包括图片含义这样的词, 中文也一样. eslint: jsx-a11y/img-redundant-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 />
避免使用数组的index来作为属性key的值,推荐使用唯一ID
why?
https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
对于所有非必须的属性,总是手动去定义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,
};
尽可能少地使用扩展运算符
7. Refs
总是在Refs里使用回调函数. eslint: react/no-string-refs
// bad
<Foo
ref="myRef"
/>
// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>
8.括号
将多行的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>;
}
9.Tag
对于没有子元素的标签来说总是自己关闭标签. 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"
/>
10. 函数
使用箭头函数来获取本地变量.
function ItemList(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={() => doSomethingWith(item.name, index)}
/>
))}
</ul>
);
}
当在 render()
里使用事件处理方法时,提前在构造函数里把 this
绑定上去. eslint: react/jsx-no-bind
// 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模块中,不要给所谓的私有函数添加 _ 前缀,本质上它并不是私有的.
// 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 />);
}
Ordering React 模块生命周期
如何定义 propTypes, defaultProps, contextTypes, 等等其他属性...
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;
参考文档
https://github.com/JasonBoy/javascript/tree/master/react
https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules