您如何决定,如何根据我们组件的目的/尺寸/道具/行为在这三个之间进行选择?
从自定义方法扩展
React.PureComponent或从
React.Component自定义
shouldComponentUpdate方法扩展具有性能影响。使用无状态功能组件是“体系结构”的选择,并且没有开箱即用的性能优势。
对于需要易于重用的简单的仅表示组件,首选无状态功能组件。这样,您可以确定它们与实际的应用程序逻辑是分离的,它们非常容易测试,并且没有意外的副作用。例外是如果出于某种原因有 很多 它们,或者确实需要优化它们的渲染方法(因为您无法
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进行优化,通常应仅在遇到性能问题时才开始研究(避免过早的优化)。根据经验,我总是尝试在应用程序处于工作状态并且已经实现了大多数功能之后进行这些优化。当实际遇到问题时,专注于性能问题要容易得多。
更多细节
功能性无状态组件:
这些仅使用函数定义。由于无状态组件没有内部状态,因此输出(呈现的内容)仅取决于作为此函数的输入给出的props。
优点:
在React中定义组件的最简单的方法。如果您不需要管理任何状态,为什么还要麻烦类和继承呢?函数和类之间的主要区别之一是,使用函数可以确保输出仅取决于输入(而不取决于先前执行的任何历史记录)。
理想情况下,在您的应用中,您应该致力于拥有尽可能多的无状态组件,因为这通常意味着您将逻辑移到了视图层之外,然后将其移至了redux之类,这意味着您可以测试真实的逻辑而不必渲染任何东西(更容易测试,更可重用等)。
缺点:
没有生命周期方法。您没有办法定义
componentDidMount
和其他朋友。通常,您可以在层次结构中较高的父级组件中执行此操作,以便可以将所有子级变为无状态的子级。由于您无法定义,因此无法手动控制何时需要重新渲染
shouldComponentUpdate
。每当组件接收到新的道具时,就会进行重新渲染(无法进行浅比较等)。将来,React可以自动优化无状态组件,因为现在可以使用一些库。由于无状态组件只是函数,因此基本上是“函数记忆”的经典问题。不支持引用:https : //github.com/facebook/react/issues/4936
扩展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。如果在父组件中
<UserAvatarusername="fabio" imageUrl="http://foo.com/fabio.jpg"/>使用相同的道具进行渲染,则
render即使输出完全相同,React也会每次调用。请记住,尽管React实现了dom
diff,所以DOM实际上不会被更新。尽管如此,执行dom diff仍然很昂贵,因此在这种情况下这将是浪费。
如果
UserAvatar组件扩展
PureComponent,则执行浅表比较。并且因为props和nextProps是相同的,所以
render根本不会被调用。
关于React中“纯”的定义的注释:
通常,“纯函数”是在给定相同输入的情况下始终对相同结果求值的函数。输出(对于React,这就是方法返回的结果
render)不依赖于任何历史记录/状态,也没有任何副作用(在函数外部更改“世界”的操作)。
在React中,如果您将“无状态”组件称为永不调用
this.setState且不使用的组件,那么根据上述定义,无状态组件不一定是纯组件
this.state。
实际上,在中
PureComponent,您仍然可以在生命周期方法中执行副作用。例如,您可以在内部发送ajax请求
componentDidMount,也可以执行DOM计算以动态调整内div的高度
render。
“哑巴组件”的定义具有更多“实用”的含义(至少在我的理解中):一个哑巴组件通过道具“被告知”父组件要做什么,并且不知道该怎么做,而是使用道具回调。
“smart”示例
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> }}“dumb””示例
AvatarComponent:
class AvatarComponent extends Component { render () { <div onClick={this.props.onExpandAvatar}> {this.props.loading && <div className="spinner" />} <img src={this.props.username} /> </div> }}最后,我会说“dumb””,“无状态”和“纯净”是完全不同的概念,有时可能会重叠,但不一定,这主要取决于您的用例。



