前言
技术团队中每个开发人员的水平不同,技术关注点不同,行为习惯不同,如果没有一份统一的代码规范参照和约束,那么项目中的代码将会风格迥异,阅读困难,甚至难以维护。
为保证代码质量和风格统一,特参考通用做法和常见场景制定此《React 代码规范》。
注:本规范适合场景默认使用Tyepescript开发。
-- 修订时间: 2021-09-18
基础规则
-【should/应当】一个文件声明一个组件
-【must/必须】文件后缀和文件内容一致
如果有内容模版则使用.tsx,纯代码使用 .ts
-【must/必须】不使用state的组件声明为函数组件
函数组件在React中有着特殊的地位,也有可能得到更多的内部优化(效率)。
// Bad
class NextNumber {
render() {
return <span>{this.props.value + 1}</span>
}
}
// Good
let NextNumber = ({value}) => <span>{value + 1}</span>;
-【must/必须】每一个文件以 export default 的形式暴露一个组件。
如果一个文件中存在多个组件,则export default只允许暴露一个组件,其它组件均定义为内部组件。
-【must/必须】每个存放组件的目录使用一个 index.ts 以命名导出的形式暴露所有组件。
// Bad
import Foo from './Foo/index';
// Good
import Foo from './Foo';
基础命名规则
- 组件名称: 推荐使用大驼峰【PascalCase】命名;
- 属性名称: React DOM 使用小驼峰【camelCase】命令来定义属性的名称,而不使用 HTML 属性名称的命名约定;
- style 样式属性: 采用小驼峰【camelCase】命名属性;
- 文件名称:组件名称与文件名称保持相同;
// 类组件名称
MyComponent
// 函数组件名称
useSomeFunction
// 属性名称
onClick
// 样式属性
backgroundColor
- 组件的代码顺序
class Example extends Component {
// 静态属性
static displayName = 'ExampleComponent'
static propTypes = {}
static contextTypes = {}
state defaultProps = {}
static state = {}
...其它静态属性
// 实例属性
someVariable = ''
...其它实例属性
// 构造函数
constructor
// 声明周期钩子函数
// 按照它们执行的顺序
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentDidMount
componentDidUpdate
componentWillUnmount
// 事件处理方法
handleClick = (e) => { ... }
//其它方法
someFunction() { ... }
// 最后,render 方法
render() { ... }
}
编码命名规范说明
- 通用命名方法
命名方法 | 举例 |
---|---|
Camel命名法 | thisIsAnApple |
Pascal命名法 | ThisIsAnApple |
注意:在命名时尽量简洁明了,用英文表达(绝对不能用拼音或者首字母拼写)
- 各类型命名规范
名称 | 命名方法 | 词汇种类 | 说明 | 举例 |
---|---|---|---|---|
局部变量名 | Camel命名法 | 名词 | fristName | |
参数名 | Camel命名法 | 名词 | fristName | |
方法/属性 | Camel命名法 | 名词 | fristName | |
常量名 | 下划线+全体大写 | 名词 | ADD_METHOD | |
类名 | Pascal命名法 | 名词 | fristName | |
局部变量名 | Camel命名法 | 名词 | AtiveDic | |
Bool类型 | Camel命名法 | 名词 | 前缀:is/has | hasChild/isFixed |
- 函数与方法的命名
动词 | 含义 | 举例 |
---|---|---|
can | 判断是否可以执行某个权限 | canLogin |
has | 判断是否含有某个值 | hasToken |
is | 判断是否为某个值 | isShowModel |
get | 获取某个值 | getUserId |
set | 设置某个值 | setCookie |
load | 加载某些数据 | loadList |
update | 更新某些数据 | updateUserInfo |
del | 删除某些数据 | delMenu |
- 遍历
数组遍历
let a = [1,2,3,4,5];
// Bad
for (let i = 0; i < a.length; i++) {
console.log(a[i]);
}
// Good
a.forEach(e => {
console.log(e);
})
// OR
a.map(e => {
console.log(e);
});
对象遍历
let a = {name:'Lucy', age:32, sex:'女'};
// Bad
for (o in a) {
console.log(a[o]);
}
// Good
for (o in a) {
if (a.hasOwnProperty(o)) {
console.log(a[o]);
}
}
TSX规则
- 没有子节点的非DOM组件使用自闭合语法。
// Bad
<Foo></Foo>
<Foo/>
<Foo />
<Foo
/>
// Good
<Foo />
- 保持起始和结束标签在同一层缩进。
对于标签前面有其它语句(如return的情况,使用括号进行换行和缩进)。
// Bad
class Message {
render() {
return <div>
<span>Hello World</span>
</div>;
}
}
// Good
class Message {
render() {
return (
<div>
<span>Hello World</span>
</div>
);
}
}
- 对于直接return的函数组件,可以直接使用括号而省去大括号和return关键字
let Message = () => (
<div>
<span>Hello World</span>
</div>
);
- 对于多属性需要换行,从第一个属性开始,每个属性一行
// 没有子节点
<SomeComponent
longProp={longProp}
anotherLongProp={anotherLongProp}
/>
// 有子节点
<SomeComponent
longProp={longProp}
anotherLongProp={anotherLongProp}
>
<SomeChild />
<SomeChild />
</SomeComponent>
// Bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
- 以字符串字面量作为值的属性使用双引号("),在其它类型表达式中的字符串使用单引号(')。
// Bad
<Foo bar='bar' />
<Foo style={{width: "20px"}} />
// Good
<Foo bar="bar" />
<Foo style={{width: '20px'}} />
- 对于值为true的属性,省去值部分。
// Bad
<Foo visible={true} />
// Good
<Foo visible />
- 对于需要使用key的场合,提供一个唯一标识作为key属性的值,禁止使用可能会变化的属性(如索引)。当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key,但是要主要如果列表项目的顺序可能会变化,如果使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
key属性是React在进行列表更新时的重要属性,如该属性会发生变化,渲染的性能和正确性都无法得到保证。一般如果缺少key,编译运行会有警告⚠️
// Bad
{list.map((item, index) => <Foo key={index} {...item} />)}
// Good
{list.map(item => <Foo key={item.id} {...item} />)}
// Bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// Good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
- 使用箭头函数(arrow function)为组件绑定事件处理器
// Bad
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
console.log('this is:', this);
}
<button onClick={this.handleClick}> Click me </button>
// Good
handleClick = () => {
console.log('this is:', this);
}
<button onClick={this.handleClick}> Click me </button>
- 避免在TSX的属性值中直接使用对象和函数表达式。
在TSX的属性值中使用对象、函数表达式会造成每一次的对象引用不同,导致不必要的渲染。
// Bad
class WarnButton {
alertMessage(message) {
alert(message);
}
render() {
return <button type="button" onClick={() => this.alertMessage(this.props.message)}>提示</button>
}
}
// Good
class WarnButton {
alertMessage = () => {
alert(this.props.message);
}
render() {
return <button type="button" onClick={this.alertMessage}>提示</button>
}
}
- 将TSX的层级控制在3层以内。
TSX提供了基于组件的便携的复用形式,因此可以通过将结构中的一部分封装为一个函数组件来很好地拆分大型复杂的结构。层次过深的结构会带来过多缩进、可读性下降等缺点。如同控制函数内代码行数和分支层级一样,对TSX的层级进行控制可以有效提升代码的可维护性。
// Bad
let List = ({items}) => (
<ul>
{
items.map(item => (
<li>
<header>
<h3>{item.title}</h3>
<span>{item.subtitle}</span>
</header>
<section>{item.content}</section>
<footer>
<span>{item.author}</span>@<time>{item.postTime}</time>
</footer>
</li>
))
}
</ul>
);
// Good
let Header = ({title, subtitle}) => (
<header>
<h3>{title}</h3>
<span>{subtitle}</span>
</header>
);
let Content = ({content}) => <section>{content}</section>;
let Footer = ({author, postTime}) => (
<footer>
<span>{author}</span>@<time>{postTime}</time>
</footer>
);
let Item = item => (
<div>
<Header {...item} />
<Content {...item} />
<Footer {...item} />
</div>
);
let List = ({items}) => (
<ul>
{items.map(Item)}
</ul>
);
- State相关
不要直接修改 state
除了 state 初始化外,其它地方修改 state,需要使用 setState( ) 方法,否则如果直接赋值,则不会重新渲染组件。
// Bad
this.state.comment = 'hello';
// Good
this.setState({comment: 'Hello'});
State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState( ) 调用合并成一个调用;因为 this.props 和 this.state 可能会异步更新,所以这种场景下需要让 setState() 接收一个函数而不是一个对象 。
// Bad
this.setState({
counter: this.state.counter + this.props.increment,
});
// Good
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- 所有组件均需声明propTypes
propsTypes在提升组件健壮性的同时,也是一种类似组件的文档的存在,有助于代码的阅读和理解。
如无必要,使用静态属性语法声明propsTypes、contextTypes、defaultProps和state。
// Bad
class Example extends React.Component {
render() {
// ...
}
}
Example.propTypes = {
name: PropTypes.string
};
// Good
class Example extends React.Component {
static defaultProps = {
name: 'stranger'
}
render() {
// ...
}
}