专栏首页code秘密花园Airbnb React/JSX 风格指南

Airbnb React/JSX 风格指南

Airbnb React/JSX 风格指南

这个指南大部分基于现在在JavaScript中流行的标准,尽管有些约定(如: async/await 或 class 的 static 字段)根据具体情况也会被引入或者被禁止。当前这个指南不包括也不推荐任何 ECMAScript stage-3(第三阶段提案) 之前的内容。

目录

  1. 基本规则(Basic Rules)
  2. Class vs `React.createClass` vs stateless
  3. 混合(Mixins)
  4. 命名(Naming)
  5. 声明(Declaration)
  6. 对齐(Alignment)
  7. 引号(Quotes)
  8. 空格(Spacing)
  9. 属性(Props)
  10. 引用(Refs)
  11. 括号(Parentheses)
  12. 标签(Tags)
  13. 方法(Methods)
  14. 排序(Ordering)
  15. `isMounted`

Basic Rules

  • 每个文件只包含一个 React 组件
    • 然而,在一个文件里包含多个没有 state 或纯组件是允许的。 eslint: `react/no-multi-comp`.
  • 经常用 JSX 语法。
  • 不要用 React.createElement, 除非你从一个非 JSX 文件中初始化 app。

Class vs `React.createClass` vs stateless

  • 如果你要用 state 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>; } } 如果你没有使用 state、 refs ,最好用正常函数(不是箭头函数)而不是 class: // 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.

Why? mixins 会引入一些隐含依赖,导致命名冲突,会导致滚雪球式的复杂度。大多数情况下,mixins 都可以通过组件,高阶组件 HOC或者工具模块更好的实现。

Naming

  • 扩展名: 用 .jsx 作为组件扩展名。
  • 文件名: 用大驼峰作为文件名,如: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';
  • 高阶组件HOC命名: 用高阶组件名和传入的组件名组合作为生成的组件的 displayName。 举个例子,一个高阶组件 withFoo(), 当传入一个组件 Bar 应该生成一个新的组件,他的 displayName 属性是 withFoo(Bar)。 Why? 组件的 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 组件的属性名表达不同的意义 Why? 人们期望 styleclassName 这种属性代表一个明确的意义。 为应用程序的一个子集改变此API会使代码的可读性降低,维护性降低,并可能导致错误。 // bad <MyComponent style="fancy" /> // bad <MyComponent className="fancy" /> // good <MyComponent variant="fancy" />

Declaration

  • 不要通过 displayName 命名组件。最好通过引用命名组件。 // bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }

Alignment

  • 对 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 里面的标签正常缩进 <Foo superLongParam="bar" anotherSuperLongParam="baz" > <Quux /> </Foo> // bad {showButton && <Button /> } // bad { showButton && <Button /> } // good {showButton && ( <Button /> )} // good {showButton && <Button />}

Quotes

  • 在 JSX 属性中用双引号("),但是在js里用单引号(')。eslint: jsx-quotes Why? 正常的 HTML 属性也通常使用双引号而不是单引号,所以 JSX 属性也使用这个约定。 // bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} />

Spacing

  • 在自闭和标签内空一格。 eslint: no-multi-spaces, react/jsx-tag-spacing // bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />
  • JSX 里的大括号不要空格。 eslint: react/jsx-curly-spacing // bad <Foo bar={ baz } /> // good <Foo bar={baz} />

Props

  • props 用小驼峰 // bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} />
  • 如果 prop 的值是 true 可以忽略这个值,直接写 prop 名就可以。 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 Why? 因为屏幕阅读器已经将 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`

Why? 使用屏幕阅读器和键盘的人使用的键盘快捷键和键盘命令之间的不一致使得可访问性变得复杂。

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

  • 避免用数组下标作为 key 属性,推荐用稳定的 ID

Why? 不使用稳定杆的 ID is an anti-pattern 会对组件性能产生消极影响,并且组件状态容易出现问题。 如果数组元素可能会发生变化,我们不推荐使用下标作为key。

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

  • 对于所有非必须属性,定义一个明确的默认值。

Why? propTypes 是一个文档形式,同时提供默认属性意味着使用者不需要假定那么多值。另外,这也意味着你的代码可以忽略类型检查。

// bad function SFC({ foo, bar, children }) { return ¨K0K ; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; // good function SFC({ foo, bar, children }) { return ¨K1K ; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; SFC.defaultProps = { bar: '', children: null, };

  • 少用props扩展运算符,既 {…props}

Why? 除非你更喜欢把不需要的props属性传入组件。而且对于 v15.6.1 及更早以前的 React, 你只能给DOM元素传非HTML属性的props。

例外:

  • HOC 是代理 props 并且提成了propTypes function HOC(WrappedComponent) { return class Proxy extends React.Component { Proxy.propTypes = { text: PropTypes.string, isLoading: PropTypes.bool }; render() { return <WrappedComponent {...this.props} /> } } }
  • 扩展一个已知的,有明确属性的对象也是可以的。这个对用 Mocha 的 beforeEach 函数做单测时尤其有用。 export default function Foo { const props = { text: '', isPublished: false } return (<div {...props} />); } 使用说明: 尽可能过滤出不需要的属性。同时用prop-type-exact去帮助避免bug。 // bad render() { const { irrelevantProp, ...relevantProps } = this.props; return <WrappedComponent {...this.props} /> } // good render() { const { irrelevantProp, ...relevantProps } = this.props; return <WrappedComponent {...relevantProps} /> }

Refs

  • 推荐用 ref callback 函数。 eslint: react/no-string-refs // bad <Foo ref="myRef" /> // good <Foo ref={(ref) => { this.myRef = ref; }} />

Parentheses

  • 当 JSX 标签有多行时,用圆括号包起来。eslint: react/jsx-wrap-multilines <br />// bad<br />render() {<br /> return ;<br />}</p> <p>// good<br />render() {<br /> return (<br /> <br /> );<br />}</p> <p>// good, 单行可以直接写<br />render() {<br /> const body = </p></li> </ul> ¨K2K ; return <MyComponent>{body}</MyComponent>; } Tags Methods Ordering `isMounted` Why? [isMounted 是反模式][anti-pattern], 这个在 ES6 class 里不允许的,而且即将被官方废弃。
    • 不要用 isMounted。 eslint: `react/no-is-mounted`
    • 如何定义 propTypesdefaultPropscontextTypes 等… 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`
    • class extends React.Component 内部属性的顺序:
    • 用箭头函数关闭局部变量。 function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); }
    • 在构造函数里绑定事件处理函数。 eslint: react/jsx-no-bind Why? render 函数中的绑定调用在每次 render 的时候都会创建一个新的函数。 // 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 组件里使用下划线作为内部方法名前缀。 Why? 下划线前缀有时候在其他语言里被用于表示私有。但是 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 />); }
    • 当没有子元素时,最好用自闭合标签。 eslint: react/self-closing-comp // bad <Foo variant="stuff"></Foo> // good <Foo variant="stuff" />
    • 如果你的组件有多行属性,用他的闭合标签单独作为结束行。 eslint: react/jsx-closing-bracket-location // bad <Foo bar="bar" baz="baz" /> // good <Foo bar="bar" baz="baz" />
    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 如: onClickSubmit()onChangeDescription()
    19. getter methods for `render` 如: getSelectReason()getFooterContent()
    20. optional render methods 如: renderNavigation()renderProfilePicture()
    21. render
    22. 可选的 static 方法
    23. constructor
    24. getChildContext
    25. componentWillMount
    26. componentDidMount
    27. componentWillReceiveProps
    28. shouldComponentUpdate
    29. componentWillUpdate
    30. componentDidUpdate
    31. componentWillUnmount
    32. clickHandlers or eventHandlers 如: onClickSubmit()onChangeDescription()
    33. getter methods for `render` 如: getSelectReason()getFooterContent()
    34. optional render methods 如: renderNavigation()renderProfilePicture()
    35. render

本文分享自微信公众号 - code秘密花园(code_mmhy)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

推荐阅读

  • 「网安夜校」开课啦!多门网络安全课程开启限时优惠报名

    众志成城,共抗疫情。腾讯安全联合腾讯云大学、腾讯课堂启动「网安夜校」,为大家提供限时优惠的网络安全课程。欢迎网络安全从业者和信息安全专业学生报名参加学习,快速充电提升自我。

    腾讯安全
    安全培训腾讯云大学
  • Flink源码走读(一):Flink工程目录

    导语 | Flink已经成为未来流计算趋势,目前在很多大厂已经有了大规模的使用。最近在学习Flink源码,就想把自己学习的过程分享出来,希望能帮助到志同道合的朋友。开始阅读源码,说明读者已经对flink的基本概念有一些了解,这里就不再重复介绍Flink了。本文作为学习过程的第一章,首先对Flink的工程目录做一个解读,了解了工程下各个模块的作用,才能在遇到问题时准确定位到代码,进一步学习。

    2011aad
    大数据解决方案
  • Flink源码走读(二):Flink+Kafka实现端到端Exactly Once语义

    Flink通过Checkpoint机制实现了消息对状态影响的Exactly Once语义,即每条消息只会影响Flink内部状态有且只有一次。但无法保证输出到Sink中的数据不重复。以图一所示为例,Flink APP收到Source中的A消息,将其转化为B消息输出到Sink,APP在处理完A1后做了一次Checkpoint,假设APP在处理到A4时发生错误重启,APP将会重新从A2开始消费并处理数据,就会导致B2和B3重复输出到Sink中两次。

    2011aad
    大数据解决方案Kafka
  • kubernetes系列教程(十九)使用metric-server让HPA弹性伸缩愉快运行

    kubernetes监控指标大体可以分为两类:核心监控指标和自定义指标,核心监控指标是kubernetes内置稳定可靠监控指标,早期由heapster完成,现由metric-server实现;自定义指标用于实现核心指标的扩展,能够提供更丰富的指标支持,如应用状态指标,自定义指标需要通过Aggregator和k8s api集成,当前主流通过promethues实现。

    HappyLau谈云计算
    Kubernetes容器微服务微服务架构腾讯微服务平台 TFS
  • 三分钟入坑指北 🔜 Docsify + Serverless Framework 快速创建个人博客系统

    之前由于学摄影的关系,为了提高自己的审美,顺便锻炼下自己的英文能力,翻译了不少国外艺术类的 文章。最近一直想搭一个个人博客来存放这些内容,又懒得折腾建站,遂一直搁置。

    Aceyclee
    ServerlessHTML网站GitGitHub
  • NVM作为主存上对数据库管理系统的影响

    implications of non-volatile memory as primary storage for database management systems

    yzsDBA
    存储缓存数据库数据结构SQL
  • DevOps平台架构演进

    附最新架构图https://www.processon.com/view/5cbd897de4b0bab90962c435

    我思故我在
    DevOps 解决方案微服务架构架构设计
  • 【腾讯云AI小程序大赛】中山大学作品《小耳朵天使》

    ----------------------------------------------------------------------------------

    陈华山
    小程序 · 云开发小程序语音识别文字识别对话机器人
  • Kona JDK 在腾讯大数据领域内的实践与发展

    经常听人谈到 OpenJDK,那它到底是什么呢?相信大家都听说过 Java SE、ME、EE等规范, 通常意义上对 Open JDK 的定义指:Java SE规范的一个免费和开源参考实现。

    腾小云
    JDKJavaJVM大数据Oracle
  • 公告丨腾讯安全产品更名通知

    为了更好地为政企客户的安全保驾护航,腾讯安全即日起更新旗下身份安全、网络安全、终端安全、应用安全、数据安全、业务安全、安全管理、安全服务等八类安全产品的命名,致力于打造全栈安全产品“货架”,让客户选购安全产品/服务更加便捷,更快地找到合适的安全产品,从而对自身的安全建设“对症下药”。

    腾讯安全
    DDoS 防护应用安全 MS验证码(业务安全)应用安全(移动安全)漏洞扫描服务

扫码关注云+社区

领取腾讯云代金券