Skip to content

Latest commit

 

History

History
 
 

react

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Airbnb React/JSX 代码规范

一套对于 React 和 JSX 基本合理的规范

本指南是主要基于目前流行的 JavaScript 标准,因此一些惯例(如:async/await 或静态类字段)可能仍然包含其中或者被禁止。 目前,处在第 3 阶段的任何内容都不包含在本指南中,也不建议使用。

  1. 基本规则
  2. Class vs React.createClass vs stateless
  3. Mixins
  4. 命名
  5. 声明模块
  6. 代码对齐
  7. 单引号和双引号
  8. 空格
  9. 属性
  10. Refs
  11. 括号
  12. 标签
  13. 方法
  14. 函数生命周期
  15. isMounted
  • 每个文件只存在一个 React 组件。
  • 始终使用 JSX 语法。
  • 除非你从一个非 JSX 文件中初始化你的应用,否则不要使用 React.createElement

⬆ 返回目录

  • 如果你的组件有内部状态或者是 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 推荐使用普通函数(不是箭头函数)而不是类定义:

    // 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>;
    }

⬆ 返回目录

为什么? Mixins 会引入隐式的依赖关系,会导致名称冲突,并致使复杂性指数级增加。 在多数情况下 mixins 能够被更好的方法替代,如:组件化,高阶组件,工具模块等。

⬆ 返回目录

  • 扩展名: 使用 .jsx 作为 React 组件的扩展名。

  • 文件名: 使用帕斯卡命名法给文件命名。 例如: ReservationCard.jsx.

  • 引用命名: 使用帕斯卡命名法给 React 组件命名并用驼峰命名法给组件实例命名。 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 的组件中的时候,应该生成一个拥有 displayNamewithFoo(Bar) 的组件。

    为什么? 一个组件的 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;
    }
  • 属性命名: 避免使用 DOM 组件属性来用作其他用途。

    为什么? 对于像 styleclassName 这样的属性我们默认它们代表一些特殊的含义。 改变这些 API 会降低你代码的可读性和可维护性,并且可能导致问题。

    // bad
    <MyComponent style="fancy" />
    
    // bad
    <MyComponent className="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 react/jsx-closing-tag-location

    // bad
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // good
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // 如果能写在一行,直接写成一行
    <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' }} />

    ⬆ 返回目录

  • 总是使用驼峰命名法命名属性名。

    // 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 属性。 如果图片是直观的, 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" />
  • 不要在 <img>alt 属性中使用 "image", "photo", 或 "picture" 这些词汇。 eslint: jsx-a11y/img-redundant-alt

    为什么? 屏幕阅读器已经把 img 元素作为图片了,因此 alt 中不需要再进行说明。

    // 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 - 不是一个 ARIA role
    <div role="datepicker" />
    
    // bad - 抽象的 ARIA role
    <div role="range" />
    
    // good
    <div role="button" />
  • 不要在元素上使用 accessKey 。 eslint: jsx-a11y/no-access-key

为什么? 设置的键盘快捷键和使用显示器和键盘的人的键盘命令不一致会导致访问的复杂性。

// bad
<div accessKey="h" />

// good
<div />
  • 避免使用数组的索引作为 key 属性的值,应该是唯一的 ID。 (why?)
// bad
{todos.map((todo, index) =>
  <Todo
    {...todo}
    key={index}
  />
)}

// good
{todos.map(todo => (
  <Todo
    {...todo}
    key={todo.id}
  />
))}
  • 总是为所有非必要的属性定义明确的 defaultProps 。

为什么? 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, children }) {
  return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node,
};
SFC.defaultProps = {
  bar: '',
  children: null,
};
  • 尽可能少使用扩展运算符。

为什么? 这样你有更大的可能将不必要的属性传递个组件。对于 React v15.6.1 和更老的版本,你可以查看 将无效的属性传递给 DOM .

例外:

  • 代理高阶组件和 propTypes 变量提升。
function HOC(WrappedComponent) {
  return class Proxy extends React.Component {
    Proxy.propTypes = {
      text: PropTypes.string,
      isLoading: PropTypes.bool
    };

    render() {
      return <WrappedComponent {...this.props} />
    }
  }
}
  • 对于已知的,明确的对象使用扩展运算符。这非常有用尤其是在使用 Mocha 测试组件的时候。
export default function Foo {
  const props = {
    text: '',
    isPublished: false
  }

  return (<div {...props} />);
}

使用注意事项:尽可能的过滤掉不必要的属性。 此外,可以使用 prop-types-exact 来帮助避免错误。

// good
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...relevantProps} />
}

// bad
render() {
  const { irrelevantProp, ...relevantProps  } = this.props;
  return <WrappedComponent {...this.props} />
}

⬆ 返回目录

  • 当存在多行时,使用括号包裹 JSX 标签。 eslint: react/jsx-wrap-multilines

    // bad
    render() {
      return <MyComponent variant="long body" foo="bar">
               <MyChild />
             </MyComponent>;
    }
    
    // good
    render() {
      return (
        <MyComponent variant="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    // good, 当存在一行时
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }

    ⬆ 返回目录

  • 使用箭头函数关闭局部变量。

    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

    为什么? 渲染路径中的绑定函数会在每个渲染器上创建一个全新的函数。

    // 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 组件的内部方法中使用下划线前缀。

    为什么? 下划线前缀有时被用作其他语言的约定来表示隐私。 但是,与其他语言不同, JavaScript 没有隐私的原生支持,所有东西都是公开的。 无论你是什么意图,在属性前添加下划线前缀并不会使它们成为私有属性,并且任何属性(有或者没有前缀)都应该被视为公共的。 可以查看问题 #1024,和 #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 方法
  2. constructor
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. 单击处理时间或者事件处理器onClickSubmit() 或者 onChangeDescription()
  12. 对于 render 的 get 方法getSelectReason() 或者 getFooterContent()
  13. 可选的 render 方法renderNavigation() 或者 renderProfilePicture()
  14. render
  • 如何定义 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;
  • 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. 单击处理时间或者事件处理器onClickSubmit() 或者 onChangeDescription()
  19. 对于 render 的 get 方法getSelectReason() 或者 getFooterContent()
  20. 可选的 render 方法renderNavigation() 或者 renderProfilePicture()
  21. render

⬆ 返回目录

isMounted

为什么? isMounted 是一种反模式,在使用 ES6 类定义的时候不可用,并且正在被正式弃用。

**[⬆ 返回目录](#table-of-contents)**