你如何决定,你如何根据我们组件的用途/大小/道具/行为在这三者之间进行选择?
使用自定义 shouldComponentUpdate 方法从 React.PureComponent 或 React.Component 扩展会影响性能。使用无状态功能组件是一种“架构”选择,并且没有任何开箱即用的性能优势(目前)。
对于需要轻松重用的简单、仅显示的组件,首选无状态功能组件。通过这种方式,您可以确定它们与实际的应用程序逻辑分离,它们非常容易测试并且它们没有意外的副作用。例外情况是,如果出于某种原因您有 很多 或者您确实需要优化它们的渲染方法(因为您不能为无状态功能组件定义 shouldComponentUpdate)。
如果您知道您的输出依赖于简单的 props/state(“简单”意味着没有嵌套数据结构,因为 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)。
根据经验,我总是在应用程序处于工作状态后尝试进行这些优化,并且大多数功能已经实现。当性能问题真正阻碍时,专注于性能问题要容易得多。
更多详情
功能性无状态组件:
这些只是使用函数定义的。由于无状态组件没有内部状态,因此输出(渲染的内容)仅取决于作为该函数输入的道具。
优点:
缺点:
没有生命周期方法。你没有办法定义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 只是以与PureRendererMixin 相同的方式实现shouldComponentUpdate。这主要是一种方便的事情,因此您不必自己实现它,因为当前/下一个状态和 props 之间的浅比较可能是最常见的场景,可以让您快速获得一些性能提升。
例子:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
如您所见,输出取决于props.imageUrl 和props.username。如果在父组件中使用相同的 props 渲染 <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />,React 每次都会调用 render,即使输出完全相同。请记住,尽管 React 实现了 dom diff,因此 DOM 不会实际更新。尽管如此,执行 dom 差异可能会很昂贵,因此在这种情况下,这将是一种浪费。
如果UserAvatar 组件改为扩展PureComponent,则执行浅比较。而且因为 props 和 nextProps 是一样的,render 根本不会被调用。
React中“纯”定义的注意事项:
一般来说,“纯函数”是一个在给定相同输入的情况下总是计算出相同结果的函数。输出(对于 React,这是 render 方法返回的内容)不依赖于任何历史/状态,也没有任何副作用(改变函数外部“世界”的操作)。
在 React 中,如果你称“无状态”组件为从不调用 this.setState 并且不使用 this.state 的组件,那么根据上面的定义,无状态组件不一定是纯组件。
事实上,在PureComponent 中,您仍然可以在生命周期方法中执行副作用。例如,您可以在componentDidMount 中发送一个ajax 请求,或者您可以执行一些DOM 计算来动态调整render 中的div 的高度。
“哑组件”定义具有更“实用”的含义(至少在我的理解中):哑组件“被告知”父组件通过 props 做什么,但不知道该怎么做但使用道具回调代替。
“智能”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>
}
}
“哑巴”AvatarComponent 的示例:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
最后我要说的是,“哑”、“无状态”和“纯”是完全不同的概念,有时可以重叠,但不一定,主要取决于您的用例。