Skip to content

Latest commit

 

History

History
496 lines (378 loc) · 13.2 KB

React.md

File metadata and controls

496 lines (378 loc) · 13.2 KB

React/JSX 代码风格指南

如何用最合理的方式写你的 React/JSX 代码

BY 张聪(dancerphil@github)

这是一篇在原文基础上演绎的译文,与原文的表达会有出入。

原文在不断的更新,本文基于 2016-07-03 的版本,last commit [36d1561]

除非另行注明,页面上所有内容采用 MIT 协议共享。

目录

  1. 基本规则 Basic Rules
  2. 类, createClass 与无状态 Class vs React.createClass vs stateless
  3. 命名 Naming
  4. 声明 Declaration
  5. 对齐 Alignment
  6. 引号 Quotes
  7. 空格 Spacing
  8. 属性 Props
  9. 引用 Refs
  10. 括号 Parentheses
  11. 标签 Tags
  12. 方法 Methods
  13. Ordering
  14. isMounted

基本规则

类, createClass 与无状态

  • 如果你有内部的状态或引用(reference),选择使用 class extends React.Component ,而不是 React.createClass ,除非你有一个非常好的理由,否则不要使用 mixin。 eslint: react/prefer-es6-class react/prefer-stateless-function

    // 差评
    const Listing = React.createClass({
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    });
    
    // 好评
    class Listing extends React.Component {
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    }

    如果你不需要内部状态和引用,选择使用一般的函数,而不是箭头函数,也不是 class:

    // 差评
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // 差评 (relying on function name inference is discouraged)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // 好评
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }

命名

  • 后缀名: 写 React 组件应当使用后缀名 .jsx

  • 文件名: 使用帕斯卡命名法,比如 ReservationCard.jsx.

  • 引用命名: 使用帕斯卡命名法命名组件,用驼峰命名法命名组件的实例。 eslint: react/jsx-pascal-case

    // 差评
    import reservationCard from './ReservationCard';
    
    // 好评
    import ReservationCard from './ReservationCard';
    
    // 差评
    const ReservationItem = <ReservationCard />;
    
    // 好评
    const reservationItem = <ReservationCard />;
  • 组件命名: 文件名和组件名保持相同。比如, ReservationCard.jsx 应当有一个名为 ReservationCard 的引用。然而对于一个某个目录的根组件来说,使用 index.jsx 作为文件名,然后用目录名称作为组件名:

    // 差评
    import Footer from './Footer/Footer';
    
    // 差评
    import Footer from './Footer/index';
    
    // 好评
    import Footer from './Footer';

声明 Declaration

  • 不要使用 displayName 来命名组件,而使用参考(reference)命名组件

    // 差评
    export default React.createClass({
      displayName: 'ReservationCard',
      // stuff goes here
    });
    
    // 好评
    export default class ReservationCard extends React.Component {
    }

对齐 Alignment

  • JSX 语法遵循以下对齐风格。 eslint: react/jsx-closing-bracket-location

    // 差评
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // 好评
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // 如果属性(props)适合写在同一行上,就把他们写在一行
    <Foo bar="bar" />
    
    // 通常子元素需要缩进
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>

引号 Quotes

  • 书写 JSX 属性值时总是使用双引号("),但是对于其他所有的 JS 代码都使用单引号。 eslint: jsx-quotes

为什么? JSX 属性 不能包括转译的引号 ,在双引号里类似 "don't" 的属性值更容易输入。 通常的 HTML 属性使用双引号, JSX 属性遵循了相同的语法

```jsx
// 差评
<Foo bar='bar' />

// 好评
<Foo bar="bar" />

// 差评
<Foo style={{ left: "20px" }} />

// 好评
<Foo style={{ left: '20px' }} />
```

空格 Spacing

  • 在你的自闭合标签里包含一个空格

    // 差评
    <Foo/>
    
    // very bad
    <Foo                 />
    
    // 差评
    <Foo
     />
    
    // 好评
    <Foo />
  • 不要在 JSX 大括号内垫上空格。 eslint: react/jsx-curly-spacing

    // 差评
    <Foo bar={ baz } />
    
    // 好评
    <Foo bar={baz} />

属性 Props

  • 永远使用驼峰命名法命名属性名

    // 差评
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // 好评
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />
  • 如果一个属性值是明确的 true ,把值省略不写。 eslint: react/jsx-boolean-value

    // 差评
    <Foo
      hidden={true}
    />
    
    // 好评
    <Foo
      hidden
    />
  • <img> 标签应当永远包括 alt 属性。除非图片是介绍性的(presentational), alt 可以为空字符串或者 <img> 必须包含 role="presentation" 。 eslint: jsx-a11y/img-has-alt

    // 差评
    <img src="hello.jpg" />
    
    // 好评
    <img src="hello.jpg" alt="Me waving hello" />
    
    // 好评
    <img src="hello.jpg" alt="" />
    
    // 好评
    <img src="hello.jpg" role="presentation" />
  • <img> alt 属性里不要使用类似 "图像 image", "照片 photo",或者 "图片 picture" 这样的词语。 eslint: jsx-a11y/img-redundant-alt

为什么?屏幕(Screenreaders)已经把 img 标签标注为图片了, 所以没有必要再在 alt 里说明这些信息

```jsx
// 差评
<img src="hello.jpg" alt="Picture of me waving hello" />

// 好评
<img src="hello.jpg" alt="Me waving hello" />
```
  • 使用有效的,非抽象的 ARIA roles 属性值。 eslint: jsx-a11y/aria-role

    // 差评 - not an ARIA role
    <div role="datepicker" />
    
    // 差评 - abstract ARIA role
    <div role="range" />
    
    // 好评
    <div role="button" />
  • 不要在元素上添加使用 accessKey 属性。 eslint: jsx-a11y/no-access-key

为什么?如果使用屏幕和键盘的人所用的键盘命令与键盘快捷键不相符合,会引起访问不一致性。

// 差评
<div accessKey="h" />

// 好评
<div />
  • 不要使用数组的 index 作为属性 key 的值,使用一个 unique ID。

为什么?

// bad
{todos.map((todo, index) =>
  <Todo
    {...todo}
    key={index}
  />
)}

// good
{todos.map(todo => (
  <Todo
    {...todo}
    key={todo.id}
  />
))}

引用 Refs

译注:引用一个组件 React 支持以下的两种方式,但是第一种是过时的向前兼容

```jsx
// 差评
<Foo
  ref="myRef"
/>

// 好评
<Foo
  ref={(ref) => this.myRef = ref}
/>
```

括号 Parentheses

  • 把 JSX 标签写在括号 () 里,如果他们超过一行。 eslint: react/wrap-multilines

    // 差评
    render() {
      return <MyComponent className="long body" foo="bar">
               <MyChild />
             </MyComponent>;
    }
    
    // 好评
    render() {
      return (
        <MyComponent className="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    // 好评, when single line
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }

标签 Tags

  • 总是自闭合(self-close)那些没有子元素的标签。 eslint: react/self-closing-comp

    // 差评
    <Foo className="stuff"></Foo>
    
    // 好评
    <Foo className="stuff" />
  • 如果你的组件有多行属性,新起一行关闭标签。 eslint: react/jsx-closing-bracket-location

    // 差评
    <Foo
      bar="bar"
      baz="baz" />
    
    // 好评
    <Foo
      bar="bar"
      baz="baz"
    />

方法 Methods

  • 使用箭头函数获取本地变量。

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={() => doSomethingWith(item.name, index)}
            />
          ))}
        </ul>
      );
    }
  • 在构造函数中就把 render 方法中需要的事件手柄绑定。 eslint: react/jsx-no-bind

为什么?每一次 render 过程中,bind 调用都会创建一个全新的函数

```jsx
// 差评
class extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv.bind(this)} />
  }
}

// 好评
class extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv} />
  }
}
```
  • 不要给内部函数使用下划线前缀 _ ,他们实际上并不是私有的

    // 差评
    React.createClass({
      _onClickSubmit() {
        // do stuff
      },
    
      // other stuff
    });
    
    // 好评
    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    }
  • 保证在你的 render 方法中返回值。 eslint: require-render-return

    // 差评
    render() {
      (<div />);
    }
    
    // 好评
    render() {
      return (<div />);
    }

isMounted

为什么? isMounted 是反模式(anti-pattern),ES6 classes 中将不再使用它,此方法将被官方逐步移除。

↑ 回到最上方