React 编码规范

前言

技术团队中每个开发人员的水平不同,技术关注点不同,行为习惯不同,如果没有一份统一的代码规范参照和约束,那么项目中的代码将会风格迥异,阅读困难,甚至难以维护。
为保证代码质量和风格统一,特参考通用做法和常见场景制定此《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() {
   // ...
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容