从 React v15.3.0 了解到,我们有一个名为 PureComponent 的新基类,可以通过内置的 PureRenderMixin 进行扩展。我的理解是,在引擎盖下,它采用了shouldComponentUpdate
内的道具的浅层比较。
现在我们有3种方法来定义React组件:
PureComponent
类Component
class 一段时间以前,我们曾将无状态组件称为纯组件,甚至是哑组件。看起来像“#34;纯粹"”这个词的整个定义。现在已经在React中发生了变化。
虽然我理解这三者之间的基本差异,但我仍然不确定何时选择。还有什么是性能影响和权衡?
这些是我希望澄清的问题:
PureComponent
类(为了性能)?Component
获得更好的效果时,我是否需要延长正常的PureComponent
课程?答案 0 :(得分:290)
使用自定义React.PureComponent
方法从React.Component
或shouldComponentUpdate
进行扩展会影响性能。使用无状态功能组件是一种建筑和#34;选择并没有任何开箱即用的性能优势。
对于需要易于重复使用的简单,仅限演示的组件,更喜欢无状态功能组件。通过这种方式,您可以确保它们与实际的应用程序逻辑分离,它们很容易测试,并且不会产生意想不到的副作用。例外情况是,如果由于某种原因你有很多,或者你真的需要优化他们的渲染方法(因为你无法为无状态功能组件定义shouldComponentUpdate
) 。
如果您知道输出取决于简单的道具/状态("简单"意味着没有嵌套数据结构,因为PureComponent执行浅层比较)并且您需要/可以扩展PureComponent
获得一些性能提升。
如果您需要通过在下一个/当前道具和州之间执行自定义比较逻辑来获得性能提升,请扩展Component
并实施您自己的shouldComponentUpdate
。例如,您可以使用lodash#isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
此外,实施您自己的shouldComponentUpdate
或从PureComponent
扩展是优化,并且像往常一样,只有在遇到性能问题时才应开始研究(avoid premature optimizations)。
根据经验,我总是尝试在应用程序处于工作状态后进行这些优化,并且已经实现了大多数功能。当实际出现问题时,关注性能问题要容易得多。
功能无状态组件:
这些仅使用函数定义。由于无状态组件没有内部状态,因此输出(所呈现的内容)仅取决于作为此函数的输入而提供的道具。
优点:
在React中定义组件的最简单方法。如果您不需要管理任何状态,为什么还要为类和继承而烦恼呢?函数和类之间的主要区别之一是,使用该函数,您确定输出仅取决于输入(而不是先前执行的任何历史记录)。
理想情况下,在您的应用中,您的目标应该是拥有尽可能多的无状态组件,因为这通常意味着您将逻辑移到视图层之外并将其移动到redux之类,这意味着您可以测试您的真实逻辑,而不必渲染任何东西(更容易测试,更可重用等)。
缺点:
没有生命周期方法。您无法定义componentDidMount
和其他朋友。通常,您在层次结构中较高的父组件中执行此操作,以便将所有子项转换为无状态子项。
无法手动控制何时需要重新渲染,因为您无法定义shouldComponentUpdate
。每次组件收到新道具时都会重新渲染(无法进行浅层比较等)。将来,React可以自动优化无状态组件,现在可以使用一些库。由于无状态组件只是功能,基本上它是"功能记忆的经典问题"。
扩展PureComponent类的组件VS扩展Component类的普通组件:
React曾经有一个PureRenderMixin
你可以附加到使用React.createClass
语法定义的类。 mixin将简单地定义shouldComponentUpdate
在下一个道具和下一个状态之间进行浅层比较,以检查是否有任何变化。如果没有任何变化,则无需执行重新渲染。
如果要使用ES6语法,则无法使用mixins。为方便起见,React引入了一个PureComponent
类,您可以继承而不是使用Component
。 PureComponent
只是以shouldComponentUpdate
的相同方式实现PureRendererMixin
。它主要是一个方便的东西,所以你不必自己实现它,因为当前/下一个状态和道具之间的浅层比较可能是最常见的场景,可以给你一些快速的性能胜利。
示例:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
正如您所看到的,输出取决于props.imageUrl
和props.username
。如果在父组件中使用相同的道具呈现<UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
,则React每次都会调用render
,即使输出完全相同。请记住,React实现了dom diffing,因此DOM实际上不会更新。尽管如此,执行dom diffing可能很昂贵,因此在这种情况下这将是一种浪费。
如果UserAvatar
组件扩展PureComponent
,则执行浅层比较。而且因为props和nextProps是相同的,所以根本不会调用render
。
关于&#34; pure&#34;的定义的注释在React:
一般来说,&#34;纯粹的功能&#34;是一个函数,在给定相同输入的情况下始终评估相同的结果。输出(对于React,render
方法返回的内容)并不依赖于任何历史/状态,并且它没有任何副作用(操作会发生变化) &#34;世界&#34;在功能之外)。
在React中,如果你调用&#34;无状态&#34;那么无状态组件不一定是上面定义的纯组件。一个永远不会调用this.setState
而且不会使用this.state
的组件。
事实上,在PureComponent
中,您仍然可以在生命周期方法中执行副作用。例如,您可以在componentDidMount
内发送ajax请求,或者您可以执行一些DOM计算以动态调整render
内div的高度。
&#34; Dumb组件&#34;定义有更多&#34;实用&#34;意思(至少在我的理解中):一个愚蠢的组件&#34;被告知&#34;如果父组件通过道具做什么,并且不知道如何做事,而是使用道具回调。
&#34; smart&#34; AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}
&#34;哑巴&#34; AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
最后我会说&#34; dumb&#34;,&#34;无国籍&#34;和&#34;纯粹&#34;是不同的概念,有时可以重叠,但不一定,主要取决于您的用例。
答案 1 :(得分:23)
我不是一个天才的反应,但根据我的理解,我们可以在以下情况下使用每个组件
无状态组件 - 这些组件没有生命周期,所以这些组件应该用于渲染父组件的重复元素,例如渲染文本列表它只显示信息,并且没有任何动作可以执行。
纯组件 - 这些是具有生命周期的项目,当给出一组特定的道具时,它们将始终返回相同的结果。当显示结果列表或特定对象数据时,可以使用这些组件,这些对象数据不具有复杂的子元素并且用于执行仅影响其自身的操作。这样一个显示用户卡列表或产品卡列表(基本产品信息),只有用户可以执行的操作是点击查看详细页面或添加到购物车。
普通组件或复杂组件 - 我使用了术语复杂组件,因为它们通常是页面级组件,并且包含许多子组件,因为每个子组件都可以表现为它自己的唯一组件因此,您无法100%确定它将在给定状态下呈现相同的结果。正如我所说,这些应该用作容器组件
答案 2 :(得分:1)
React.Component
是默认的“正常”组件。您可以使用class
关键字和extends React.Component
声明它们。将它们视为具有生命周期方法,事件处理程序和任何方法的类。
React.PureComponent
是React.Component
,其实现shouldComponentUpdate()
的功能是对其props
和state
进行浅表比较。如果您知道组件的props或状态嵌套数据已更改,并且您要重新渲染,则必须使用forceUpdate()
。因此,如果当您作为道具传递或设置为状态的数组或对象发生变化时需要重新渲染组件,则它们并不是很好。
功能组件是没有生命周期功能的组件。它们据说是无状态的,但是它们是如此干净整洁,以至于我们现在有了钩子(自React 16.8起),因此您仍然可以拥有状态。所以我想它们只是“干净的组件”。