Reactfunction无状态组件,PureComponent,组件; 有什么区别,什么时候应该用什么?

知道从React v15.3.0开始 ,我们有了一个名为PureComponent的新的基类, 用于内置PureRenderMixin 。 我所理解的是,在这个引擎之下,我们在shouldComponentUpdate里面使用了一个比较简单的道具。

现在我们有3种方法来定义一个React组件:

  1. 没有扩展任何类的function性无状态组件
  2. 一个扩展了PureComponent类的组件
  3. 一个扩展Component类的正常组件

一段时间以前,我们曾经把无状态组件称为纯组件,甚至是哑组件。 看起来像“纯”这个词的整个定义现在已经在React中改变了。

虽然我理解这三者之间的基本差异,但我仍然不确定何时select什么 。 另外,每个方面的性能影响和权衡是什么?


更新

这些是我希望得到澄清的问题:

  • 我应该select将我的简单组件定义为function性(为了简单起见)还是扩展PureComponent类(为了性能)?
  • 性能提升,我得到一个真正的权衡,我失去了简单性?
  • 当我总能使用PureComponent来获得更好的性能时,是否需要扩展正常的Component类?

你如何决定,如何根据我们的组件的目的/尺寸/道具/行为来select这三者之间的关系?

使用自定义的shouldComponentUpdate方法从React.PureComponentReact.Component进行扩展会影响性能。 使用无状态的function组件是一个“架构”的select,并没有任何性能优势开箱(还)。

  • 对于需要轻松重复使用的简单表示组件,首选无状态function组件。 通过这种方式,您可以确信它们与实际的应用程序逻辑是分离的,它们很容易testing,并且没有意想不到的副作用。 例外是,如果由于某种原因你有很多 ,或者如果你真的需要优化他们的渲染方法(因为你无法为无状态的function组件定义shouldComponentUpdate )。

  • 如果你知道你的输出依赖于简单的道具/状态(“简单”意味着没有嵌套数据结构,因为PureComponent执行浅层比较),那么扩展PureComponent AND你需要/可以得到一些性能改进。

  • 如果您需要通过执行next / current props和state之间的自定义比较逻辑来获得性能上的提升,可以扩展Component并实现自己的shouldComponentUpdate 。 例如,您可以使用lodash#isEqual快速执行深层比较:

     class MyComponent extends Component { shouldComponentUpdate (nextProps, nextState) { return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState); } } 

此外,实现您自己的shouldComponentUpdate或从PureComponent扩展是优化,像往常一样,只有当你有性能问题( 避免过早优化 ),你应该开始考虑。 作为一个经验法则,我总是尝试在应用程序处于工作状态之后进行这些优化,并且已经实现了大部分function。 当他们真正阻碍时,专注于性能问题要容易得多。

更多细节

function无状态组件:

这些只是使用一个函数来定义的。 由于无状态组件没有内部状态,输出(呈现的内容)仅取决于作为该函数input的道具。

优点:

  • 在React中定义组件的最简单的方法。 如果你不需要pipe理任何状态,为什么要打扰类和inheritance? 函数和类之间的主要区别之一是用函数确定输出只依赖于input(而不是以前执行的任何历史logging)。

  • 理想情况下,在你的应用程序中,你应该尽可能多地拥有无状态的组件,因为这通常意味着你把你的逻辑移到了视图层之外,并且把它移动到了像redux之类的东西,这意味着你可以testing你的真实逻辑而不必渲染任何东西(更容易testing,更可重用等)。

缺点:

  • 没有生命周期方法。 您无法定义componentDidMount和其他朋友。 通常情况下,您可以在层次结构较高的父组件中执行此操作,以便将所有子项都转换为无状态的子组件。

  • 没有办法手动控制什么时候需要重新渲染,因为你不能定义shouldComponentUpdate 。 每次组件接收到新的道具时都会进行重新渲染(无法进行浅层比较等)。 将来,React可以自动优化无状态组件,现在有一些库可以使用。 由于无状态组件只是function,基本上就是“function记忆”的经典问题。

  • Refs不支持: https : //github.com/facebook/react/issues/4936

一个扩展了PureComponent类的组件一个扩展了Component类的普通组件:

使用React可以将PureRenderMixin附加到使用React.createClass语法定义的类上。 mixin会简单地定义一个shouldComponentUpdate执行下一个道具和下一个道具之间的浅层比较,以检查是否有任何变化。 如果没有改变,那么就不需要执行重新渲染。

如果你想使用ES6语法,你不能使用mixins。 所以为了方便React引入了可以inheritance的PureComponent类,而不是使用ComponentPureComponent只是以与shouldComponentUpdate相同的方式实现PureRendererMixin 。 这主要是一个方便的事情,所以你不必自己实现,作为当前/下一个状态和道具之间的浅层比较可能是最常见的情况,可以给你一些快速的performance胜利。

例:

 class UserAvatar extends Component { render() { return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div> } } 

正如你所看到的,输出取决于props.imageUrlprops.username 。 如果在父组件中使用相同的道具渲染<UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> ,则React每次都会调用render ,即使输出是完全正确的一样。 请记住,虽然React实现DOM差异,所以DOM不会实际更新。 尽pipe如此,执行dom差异可能是昂贵的,所以在这种情况下,这将是一种浪费。

如果UserAvatar组件扩展了PureComponent ,则执行浅层比较。 而且因为道具和nextProps是一样的, render根本不会被调用。

React中对“纯”的定义的注释:

一般来说,“纯函数”是一个函数,在给定相同的input的情况下总是得到相同的结果。 输出(对于React,这是render方法返回的内容)不依赖于任何历史/状态,也没有任何副作用(改变函数之外的“世界”的操作)。

在React中,如果调用“无状态”的组件,而永远不会调用this.setState ,并且不使用this.state状态组件不一定是纯组件。

事实上,在PureComponent ,您仍然可以在生命周期方法中执行副作用。 例如,您可以在componentDidMount发送ajax请求,或者可以执行一些DOM计算来dynamic调整render内div的高度。

“愚蠢组件”定义有一个更“实际”的含义(至less在我的理解中):一个愚蠢的组件“被告知”由父组件通过道具做什么,不知道如何做,但使用道具callback代替。

“智能” AvatarComponent

 class AvatarComponent extends Component { expandAvatar () { this.setState({ loading: true }); sendAjaxRequest(...).then(() => { this.setState({ loading: false }); }); } render () { <div onClick={this.onExpandAvatar}> <img src={this.props.username} /> </div> } } 

“哑”的AvatarComponent

 class AvatarComponent extends Component { render () { <div onClick={this.props.onExpandAvatar}> {this.props.loading && <div className="spinner" />} <img src={this.props.username} /> </div> } } 

最后,我会说,“哑”,“无状态”和“纯”是完全不同的概念,有时可能重叠,但不一定,主要取决于你的用例。

我不是一个反应的天才,但从我的理解,我们可以在以下情况下使用每个组件

  1. 无状态组件 –这些组件是没有生命周期的组件,所以这些组件应该用于渲染父组件的重复元素,例如渲染只显示信息的文本列表,而不需要执行任何动作。

  2. 纯粹的组件 –这些是具有生命周期的项目,并且在给定特定状态时总是返回相同的结果。 当显示结果列表或者不具有复杂子元素并用于执行仅影响其自身的操作的特定对象数据时,可以使用这些组件。 这样的用户卡或产品列表(基本产品信息)的显示列表,并且只有用户可以执行的操作是点击查看详细页面或添加到购物车。

  3. 正常组件或复杂组件 –我使用的术语复杂组件,因为它们通常是页面级别的组件,并且包含大量的子组件,并且因为每个子组件都可以以其独特的方式运行,所以您不能100%确定它会在给定的状态下呈现相同的结果。 正如我通常所说,这些应该被用作容器组件