反应功能无状态组件、PureComponent、component;区别是什么?我们应该在什么时候使用什么?

我们从React v15.3.0中了解到,我们有一个名为PureComponent的新基类,它通过内置的PureRenderMixin进行扩展。我的理解是,在引擎盖下,这采用了对shouldComponentUpdate内部道具的粗略比较

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

  1. 不扩展任何类的功能性无状态组件
  2. 扩展PureComponent类的组件
  3. 扩展组件类的普通组件

很久以前,我们曾将无状态组件称为纯组件,甚至是哑组件。似乎是这个词的全部定义;纯粹的;现在,情况发生了变化

虽然我了解这三者之间的基本区别,但我仍然不确定何时选择什么。还有,每种方法的性能影响和权衡是什么


更新

以下是我希望澄清的问题:

  • 我应该选择将我的简单组件定义为功能组件(为了简单)还是扩展PureComponent类(为了性能)
  • 性能提升是否是我真正的权衡
    我输了
  • 当我可以始终使用PureComponent以获得更好的性能时,我是否需要扩展普通的组件

您如何决定,如何根据我们组件的用途/大小/道具/行为在这三者之间进行选择

React.PureComponent或从React.Component使用自定义shouldComponentUpdate方法进行扩展会影响性能。使用无状态功能组件是一种“体系结构”选择,并且没有任何开箱即用的性能优势

  • 对于需要轻松重用的简单、仅表示的组件,首选无状态功能组件。通过这种方式,您可以确保它们与实际的应用程序逻辑分离,非常容易测试,并且没有意外的副作用。例外情况是,如果出于某种原因,您有很多这样的组件,或者您确实需要优化它们的渲染方法(因为您不能为无状态功能组件定义shouldComponentUpdate

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

  • 扩展组件并实现您自己的shouldComponentUpdate,如果您需要通过在下一个/当前道具和状态之间执行自定义比较逻辑来提高性能。例如,您可以使用lodash#isEqual快速执行深度比较:

    类MyComponent扩展组件{
    shouldComponentUpdate(下一步,下一步状态){
    return!u.isEqual(this.props,nextProps)| |!uqual.isEqual(this.state,nextState);
    }
    }

此外,实现您自己的shouldComponentUpdate或从PureComponent扩展都是优化,通常只有在出现性能问题时才应该开始研究这一点(避免过早优化)。
根据经验,我总是尝试在应用程序处于工作状态后进行这些优化,大多数功能已经实现。当性能问题真正成为阻碍时,更容易关注它们

更多细节

功能性无状态组件:

这些都是使用函数定义的。由于无状态组件没有内部状态,因此输出(呈现的内容)仅取决于作为此函数输入的道具

优点:

  • 在React中定义组件的最简单方法。如果您不需要管理任何状态,为什么还要麻烦处理类和继承呢?函数和类之间的主要区别之一是,对于函数,您可以确保输出仅取决于输入(而不取决于以前执行的任何历史记录)

  • 理想情况下,在您的应用程序中,您应该尽可能多地使用无状态组件,因为这通常意味着您将逻辑移到了视图层之外,并将其移到了类似redux的位置,这意味着您可以在不必渲染任何内容的情况下测试真正的逻辑(更易于测试,更易于重用,等等)

缺点:

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

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

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

扩展PureComponent类的组件与扩展组件类的普通组件相比:

React过去有一个PureRenderMixin可以附加到使用React.createClass语法定义的类。mixin只需定义一个shouldComponentUpdate,在下一个道具和下一个状态之间执行一个粗略的比较,以检查是否有任何变化。如果没有任何更改,则无需执行重新渲染

如果要使用ES6语法,则不能使用mixin。因此,为了方便起见,我们引入了一个PureComponent类,您可以从中继承,而不是使用ComponentPureComponent只是以与pureRenderMixin相同的方式实现shouldComponentUpdate。这基本上是一件方便的事情,因此您不必自己实现它,因为当前/下一个状态和道具之间的肤浅比较可能是最常见的场景,可以让您快速获得性能胜利

例如:

类UserAvatar扩展组件{
render(){
return<div><img src={this.props.imageUrl}/>{{this.props.username}}</div>
}
}

如您所见,输出取决于props.imageUrlprops.username。如果在父组件中渲染<UserAvatar username=“fabio”imageUrl=”http://foo.com/fabio.jpg“/&gt使用相同的道具,React每次都会调用render,即使输出完全相同。请记住,React实现了dom扩散,因此dom实际上不会被更新。尽管如此,执行dom扩散可能会很昂贵,因此在这种情况下这将是一种浪费

如果UserAvatar组件扩展了PureComponent,则执行浅层比较。由于props和nextrops是相同的,所以根本不会调用render

关于React中“纯”定义的注释:

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

在React中,如果您将“无状态”组件称为从不调用this.setState且不使用this.state的组件,则根据上述定义,无状态组件不一定是纯组件

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

“Dumb components”的定义有一个更“实际”的含义(至少在我的理解中):一个Dumb component通过props被父组件“告知”要做什么,并且不知道如何做,而是使用props回调

“智能”化身组件的示例:

类虚拟组件扩展组件{
扩展化身(){
this.setState({loading:true});
sendAjaxRequest(…)。然后(()=&gt{
this.setState({loading:false});
});
}
渲染(){
<div onClick={this.expandAvatar}>
<img src={this.props.username}/>
</div>
}
}

“哑”化身组件的示例:

类虚拟组件扩展组件{
渲染(){
<div onClick={this.props.onExpandAvatar}>
{this.props.load&<div className=“spinner”/>}
<img src={this.props.username}/>
</div>
}
}

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

发表评论