Skip to content

Latest commit

 

History

History
630 lines (483 loc) · 18 KB

File metadata and controls

630 lines (483 loc) · 18 KB

Airbnb React/JSX编码规范

一份比较合理的React和JSX的编码规范

Table of Contents目录

  1. 基本规则
  2. Class vs React.createClass vs stateless
  3. Mixins
  4. 命名
  5. 组件声明
  6. 代码对齐
  7. 引号的使用
  8. 空格
  9. 属性
  10. Refs
  11. 括号
  12. 标签
  13. 函数/方法
  14. 顺序组织
  15. isMounted

基本规则

Class vs React.createClass vs stateless

  • 如果组件包含内部状态或者refs,使用 class extends React.Component 代替 React.createClass. eslint: 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>;
      }
    }

    如果组件没有状态或者refs,选择普通的声明式的function定义(不要用箭头函数)来代替class定义:

    // bad
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // bad (不鼓励这种非直观的函数声明方式)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // good
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }

Mixins

为什么? Mixins引入了隐式的依赖,会导致命名冲突, 和滚雪球式的复杂度增长.大多数mixins的使用场景都可以更优雅地实现:比如:组件化,高阶组件,工具模块等

命名

  • 扩展名: 为React components使用 .jsx扩展名.

  • 文件名: 使用 PascalCase命名方式. 比如, ReservationCard.jsx.

  • 引用名: 使用 PascalCase命名方式为组件命名,使用小驼峰命名方式为组件实例命名。eslint: 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的组件引用. 但是,对于某个目录的根组件,使用 index.jsx作为文件名并且使用目录名作为组件名:

    // bad
    import Footer from './Footer/Footer';
    
    // bad
    import Footer from './Footer/index';
    
    // good
    import Footer from './Footer';
  • 高阶组件命名: 使用高阶组件名和传入组件名的组合来作为新生成组件的displayName属性. 例如, 高阶函数 withFoo(), 当传递了组件 Bar 作为参数时,应该生成一个displayName属性值为withFoo(Bar)的新组件.

为什么? 组件的displayName可能会被开发者工具或在错误消息中使用,并且displayName的值可以清楚表达这种关系,这有助于人们理解发生了什么。

 // 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组件的属性名作为其他用途

为什么? 人们期望像“style”和“className”这样的属性应该有特定的含义。 为应用程序的一个子集更改此API会使代码的可读性和可维护性降低,并可能导致错误。.

 // bad
 <MyComponent style="fancy" />

 // good
 <MyComponent variant="fancy" />

声明

  • 不要使用 displayName作为命名式组件的名字. 使用引用名字来替代.

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

对齐

  • 使用下面的JSX对齐写法. eslint: react/jsx-closing-bracket-location

    // bad
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // good
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // 如果props适合写在一行,就保证只有一行
    <Foo bar="bar" />
    
    // 子组件保持正常的缩进
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>

引号

  • 为JSX属性值使用双引号 ("), 为其他JS使用单引号 ('). eslint: jsx-quotes

为什么? 常规HTML属性通常使用双引号而不是单引号,因此JSX属性遵从这个约定。

 // bad
 <Foo bar='bar' />

 // good
 <Foo bar="bar" />

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

 // good
 <Foo style={{ left: '20px' }} />

空格

Props

  • 为Props属性名使用小驼峰的命名规则.

    // 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
    />
  • 总是为 <img> 标签加上alt属性值. 如果图像是可展现的, alt 可以是个字符串或者 <img> 标签必须有 role="presentation"这样的属性. eslint: jsx-a11y/img-has-alt

    // 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" />
  • <img>标签的 alt属性值中不要使用诸如 "image", "photo", or "picture" 这样的字眼. eslint: jsx-a11y/img-redundant-alt

为什么? img标签本身已经代表了图像,再用这些词未免画蛇添足了.

 // bad
 <img src="hello.jpg" alt="Picture of me waving hello" />

 // good
 <img src="hello.jpg" alt="Me waving hello" />
  • 使用有效的、非抽象的 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值,使用一个唯一的值. (why?)
// bad
{todos.map((todo, index) =>
  <Todo
    {...todo}
    key={index}
  />
)}

// good
{todos.map(todo => (
  <Todo
    {...todo}
    key={todo.id}
  />
))}
  • 始终显示地定义非必须的props属性

为什么? 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 }) {
  return <div>{foo}{bar}</div>;
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};
SFC.defaultProps = {
  bar: '',
  children: null,
};

Refs

  • 总是使用refs callbacks. eslint: react/no-string-refs

    // bad
    <Foo
      ref="myRef"
    />
    
    // good
    <Foo
      ref={(ref) => { this.myRef = ref; }}
    />

括号

  • 如果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, when single line
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }

标签

  • 标签如果没有子节点,请使用自闭和标签. 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"
    />

方法

  • 使用箭头函数来替代本地变量.

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={() => doSomethingWith(item.name, index)}
            />
          ))}
        </ul>
      );
    }
  • render中需要用到的事件响应函数,请在constructor中执行作用域绑定. eslint: react/jsx-no-bind

为什么? 在render方法中绑定,会在每一次render时产生一个新的function.

 // 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 Component的内部方法

为什么? 下划线前缀有时用作其他语言的惯例来表示私有。 但是,与之不同的是,JavaScript中没有原生支持私有,一切都是公开的。 无论您的意图如何,为您的属性添加下划线前缀实际上不会将它们设置为私有,任何属性(下划线前缀或不带前缀)都应被视为公开。 有关详情,请参阅问题[#1024](https://github.com/airbnb/javascript/issues/1024)和[#490](https://github.com/airbnb/javascript/issues/490) 深入讨论。

 // bad
 React.createClass({
   _onClickSubmit() {
     // do stuff
   },

   // other stuff
 });

 // good
 class extends React.Component {
   onClickSubmit() {
     // do stuff
   }

   // other stuff
 }
  • 确保 render方法有返回值. eslint: react/require-render-return

    // bad
    render() {
      (<div />);
    }
    
    // good
    render() {
      return (<div />);
    }

顺序

  • class extends React.Component的顺序:
  1. 可选的 static methods
  2. constructor
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. 诸如 onClickSubmit() 或者 onChangeDescription()点击或者事件处理函数
  12. 诸如getSelectReason() 或者 getFooterContent()的*render getter 方法*
  13. 可选的其他渲染方法 like renderNavigation() or renderProfilePicture()
  14. render
  • 如何定义propTypes, defaultProps, contextTypes, etc...

    import React, { PropTypes } from 'react';
    
    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;
  • React.createClass的排序: eslint: react/sort-comp

  1. displayName
  2. propTypes
  3. contextTypes
  4. childContextTypes
  5. mixins
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. clickHandlers or eventHandlers like onClickSubmit() or onChangeDescription()
  19. getter methods for render like getSelectReason() or getFooterContent()
  20. optional render methods like renderNavigation() or renderProfilePicture()
  21. render

isMounted

为什么? isMounted是一种反模式, 在使用 ES6 classes写法时并不被支持,, 并且正在被官方放弃.

翻译

This JSX/React style guide is also available in other languages:

⬆ back to top