React 函数式无状态组件,PureComponent,Component;有什么区别,我们什么时候应该使用什么?
了解到,从 React v15.3.0 开始,我们有一个名为 PureComponent 的新基类,可以使用内置的 PureRenderMixin 进行扩展.我的理解是,在底层,这对 shouldComponentUpdate
中的道具进行了浅层比较.
Came to know that from React v15.3.0, we have a new base class called PureComponent to extend with PureRenderMixin built-in. What I understand is that, under the hood this employs a shallow comparison of props inside shouldComponentUpdate
.
现在我们有 3 种方法来定义 React 组件:
Now we have 3 ways to define a React component:
- 不扩展任何类的功能性无状态组件
- 一个扩展
PureComponent
类的组件 - 一个扩展
Component
类的普通组件
- Functional stateless component which doesn't extend any class
- A component that extends
PureComponent
class - A normal component that extends
Component
class
过去我们曾经将无状态组件称为纯组件,甚至是哑组件.似乎是纯"这个词的完整定义.现在在 React 中发生了变化.
Some time back we used to call stateless components as Pure Components, or even Dumb Components. Seems like the whole definition of the word "pure" has now changed in React.
虽然我了解这三者之间的基本区别,但我仍然不确定何时选择什么.另外,每种方法的性能影响和权衡是什么?
Although I understand basic differences between these three, I am still not sure when to choose what. Also what are the performance impacts and trade-offs of each?
这些是我希望得到澄清的问题:
These are the question I expect to get clarified:
- 我应该选择将我的简单组件定义为函数式(为了简单起见)还是扩展
PureComponent
类(为了性能起见)? - 我得到的性能提升是真正的权衡吗?我失去了简单?
- 当我总是可以使用
PureComponent
以获得更好的性能时,我是否需要扩展普通的Component
类?
- Should I choose to define my simple components as functional (for the sake of simplicity) or extend
PureComponent
class (for performance sake)? - Is the performance boost that I get a real trade-off for the simplicity I lost?
- Would I ever need to extend the normal
Component
class when I can always usePureComponent
for better performance?
推荐答案
你如何根据我们组件的用途/大小/道具/行为在这三者之间做出选择?
使用自定义 shouldComponentUpdate
方法从 React.PureComponent
或 React.Component
扩展会影响性能.使用无状态功能组件是一种架构"选择,并且没有任何开箱即用的性能优势(目前).
How do you decide, how do you choose between these three based on the purpose/size/props/behaviour of our components?
Extending from React.PureComponent
or from React.Component
with a custom shouldComponentUpdate
method have performance implications. Using stateless functional components is an "architectural" choice and doesn't have any performance benefits out of the box (yet).
对于需要轻松重用的简单、仅用于演示的组件,首选无状态功能组件.通过这种方式,您可以确定它们与实际的应用程序逻辑分离,它们非常容易测试并且它们没有意外的副作用.例外情况是,如果由于某种原因您有 很多 或者您确实需要优化它们的渲染方法(因为您不能为无状态功能组件定义
shouldComponentUpdate
).
For simple, presentational-only components that need to be easily reused, prefer stateless functional components. This way you're sure they are decoupled from the actual app logic, that they are dead-easy to test and that they don't have unexpected side effects. The exception is if for some reason you have a lot of them or if you really need to optimise their render method (as you can't define
shouldComponentUpdate
for a stateless functional component).
扩展 PureComponent
如果你知道你的输出依赖于简单的道具/状态(简单"意味着没有嵌套的数据结构,因为 PureComponent 执行浅比较)并且你需要/可以获得一些性能改进.
Extend PureComponent
if you know your output depends on simple props/state ("simple" meaning no nested data structures, as PureComponent performs a shallow compare) AND you need/can get some performance improvements.
扩展 Component
并实现自己的 shouldComponentUpdate
如果您需要通过在下一个/当前道具和状态之间执行自定义比较逻辑来获得一些性能提升.例如,您可以使用 lodash#isEqual 快速执行深度比较:
Extend Component
and implement your own shouldComponentUpdate
if you need some performance gains by performing custom comparison logic between next/current props and state. For example, you can quickly perform a deep comparison using lodash#isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
此外,实现您自己的 shouldComponentUpdate
或从 PureComponent
扩展都是优化,并且像往常一样,只有在遇到性能问题时才应该开始研究(避免过早的优化).根据经验,我总是在应用程序处于工作状态后尝试进行这些优化,并且大多数功能已经实现.当性能问题真正阻碍时,专注于性能问题要容易得多.
Also, implementing your own shouldComponentUpdate
or extending from PureComponent
are optimizations, and as usual you should start looking into that only if you have performance issues (avoid premature optimizations).
As a rule of thumb, I always try to do these optimisations after the application is in a working state, with most of the features already implemented. It's a lot easier to focus on performance problems when they actually get in the way.
功能性无状态组件:
这些只是使用函数定义的.由于无状态组件没有内部状态,因此输出(渲染的内容)仅取决于作为该函数输入的道具.
These are defined just using a function. Since there's no internal state for a stateless component, the output (what's rendered) only depends on the props given as input to this function.
优点:
在 React 中定义组件的最简单方法.如果您不需要管理任何状态,为什么还要为类和继承而烦恼呢?函数和类之间的主要区别之一是,对于函数,您可以确定输出仅取决于输入(而不取决于先前执行的任何历史记录).
Simplest possible way of defining a component in React. If you don't need to manage any state, why bother with classes and inheritance? One of the main differences between a function and a class is that with the function you are sure the output depends only on the input (not on any history of the previous executions).
理想情况下,您的应用程序应该尽可能多地使用无状态组件,因为这通常意味着您将逻辑移到视图层之外并将其移至 redux 之类的东西,这意味着您可以测试您的真实无需渲染任何东西的逻辑(更容易测试,更可重用等).
Ideally in your app you should aim to have as many stateless components as possible, because that normally means you moved your logic outside of the view layer and moved it to something like redux, which means you can test your real logic without having to render anything (much easier to test, more reusable, etc.).
缺点:
没有生命周期方法.你没有办法定义
componentDidMount
和其他朋友.通常,您在层次结构中较高的父组件中执行此操作,这样您就可以将所有子组件变成无状态的组件.
No lifecycle methods. You don't have a way to define
componentDidMount
and other friends. Normally you do that within a parent component higher in the hierarchy so you can turn all the children into stateless ones.
无法手动控制何时需要重新渲染,因为您无法定义 shouldComponentUpdate
.每次组件接收到新的道具时都会发生重新渲染(无法进行浅比较等).未来,React 可以自动优化无状态组件,现在有一些库可以使用.由于无状态组件只是函数,基本上就是函数记忆"的经典问题.
No way to manually control when a re-render is needed, since you can't define shouldComponentUpdate
. A re-render happens every time the component receives new props (no way to shallow compare, etc.). In the future, React could automatically optimise stateless components, for now there's some libraries you can use. Since stateless components are just functions, basically it's the classic problem of "function memoization".
不支持引用:https://github.com/facebook/react/问题/4936
扩展 PureComponent 类的组件 VS 扩展 Component 类的普通组件:
React 曾经有一个 PureRenderMixin
,您可以将其附加到使用 React.createClass
语法定义的类.mixin 将简单地定义一个 shouldComponentUpdate
在下一个 props 和下一个状态之间进行浅比较,以检查是否有任何变化.如果没有任何变化,则无需执行重新渲染.
React used to have a PureRenderMixin
you could attach to a class defined using React.createClass
syntax. The mixin would simply define a shouldComponentUpdate
performing a shallow comparison between the next props and the next state to check if anything there changed. If nothing changes, then there's no need to perform a re-render.
如果你想使用 ES6 语法,你不能使用 mixins.所以为了方便起见,React 引入了一个 PureComponent
类,你可以继承而不是使用 Component
.PureComponent
只是实现 shouldComponentUpdate
的方式与 PureRendererMixin
相同.这主要是一种方便的事情,因此您不必自己实现它,因为当前/下一个状态和 props 之间的浅比较可能是最常见的场景,可以让您快速获得一些性能提升.
If you want to use the ES6 syntax, you can't use mixins. So for convenience React introduced a PureComponent
class you can inherit from instead of using Component
. PureComponent
just implements shouldComponentUpdate
in the same way of the PureRendererMixin
. It's mostly a convenience thing so you don't have to implement it yourself, as a shallow comparison between current/next state and props is probably the most common scenario that can give you some quick performance wins.
例子:
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 diff,因此 DOM 不会实际更新.尽管如此,执行 dom 差异可能会很昂贵,因此在这种情况下,这将是一种浪费.
As you can see the output depends on props.imageUrl
and props.username
. If in a parent component you render <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
with the same props, React would call render
every time, even if the output would be exactly the same. Remember though that React implements dom diffing, so the DOM would not be actually updated. Still, performing the dom diffing can be expensive, so in this scenario it would be a waste.
如果 UserAvatar
组件扩展了 PureComponent
,则执行浅比较.而且因为 props 和 nextProps 是一样的,所以根本不会调用 render
.
If the UserAvatar
component extends PureComponent
instead, a shallow compare is performed. And because props and nextProps are the same, render
will not be called at all.
React中纯"定义的注意事项:
一般来说,纯函数"是一个在给定相同输入的情况下总是计算出相同结果的函数.输出(对于 React,即 render
方法返回的内容)不依赖于任何历史/状态,也没有任何副作用(改变外部世界"的操作函数).
In general, a "pure function" is a function that evaluates always to the same result given the same input. The output (for React, that's what is returned by the render
method) doesn't depend on any history/state and it doesn't have any side-effects (operations that change the "world" outside of the function).
在 React 中,如果你将一个从不调用 this.setState
并且不使用 this 的组件称为无状态",那么根据上面的定义,无状态组件不一定是纯组件.状态
.
In React, stateless components are not necessarily pure components according to the definition above if you call "stateless" a component that never calls this.setState
and that doesn't use this.state
.
事实上,在 PureComponent
中,您仍然可以在生命周期方法中执行副作用.例如,您可以在 componentDidMount
中发送一个 ajax 请求,或者您可以执行一些 DOM 计算来动态调整 render
中 div 的高度.
In fact, in a PureComponent
, you can still perform side-effects during lifecycle methods. For example you could send an ajax request inside componentDidMount
or you could perform some DOM calculation to dynamically adjust the height of a div within render
.
哑组件"定义具有更实用"的含义(至少在我的理解中):哑组件被告知"父组件通过 props 做什么,但不知道该怎么做但使用道具回调代替.
The "Dumb components" definition has a more "practical" meaning (at least in my understanding): a dumb component "gets told" what to do by a parent component via props, and doesn't know how to do things but uses props callbacks instead.
智能"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>
}
}
最后我要说的是,哑"、无状态"和纯"是完全不同的概念,有时可以重叠,但不一定,主要取决于您的用例.
In the end I would say that "dumb", "stateless" and "pure" are quite different concepts that can sometimes overlap, but not necessarily, depending mostly on your use case.
相关文章