首页 文章

反应功能无状态组件,PureComponent,Component;有什么区别,什么时候应该使用什么?

提问于
浏览
161

React v15.3.0 开始知道,我们有一个名为 PureComponent 的新基类,用 PureRenderMixin 内置扩展 . 我的理解是,在引擎盖下,它采用了 shouldComponentUpdate 内部的道具浅层比较 .

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

  • 功能无状态组件,不扩展任何类

  • 扩展 PureComponent 类的组件

  • 扩展 Component 类的普通组件

一段时间后,我们习惯将无状态组件称为Pure Components,甚至是Dumb Components . 似乎“纯”这个词的整个定义现在在React中已经改变了 .

虽然我理解这三者之间的基本差异,但我仍然不确定 when to choose what . 每个产品的性能影响和权衡是什么?


更新:

这些是我希望澄清的问题:

  • 我应该选择将我的简单组件定义为功能(为简单起见)还是扩展 PureComponent 类(为了性能)?

  • 性能提升是否因为我丢失的简单性而得到了真正的 balancer ?

  • 当我总是可以使用 PureComponent 以获得更好的性能时,我是否需要扩展正常的 Component 类?

2 回答

  • 19

    我不是一个天才的反应,但从我的理解,我们可以在以下情况下使用每个组件

    • Stateless component -- 这些组件没有't have life-cycle so those components should be used in rendering repeat element of parent component such as rendering the text list which just displays the information and doesn'要执行任何操作 .

    • Pure component -- 这些是具有生命周期的项目,当给出一组特定的道具时,它们将始终返回相同的结果 . 当显示结果列表或不具有复杂子元素并用于执行仅影响其自身的操作的特定对象数据时,可以使用这些组件 . 这样一个显示用户卡列表或产品列表卡(基本产品信息),只有动作用户可以执行的是点击查看详细页面或添加到购物车 .

    • Normal Components or Complex Components -- 我使用了术语复杂组件,因为它们通常是页面级组件,并且包含许多子组件,并且因为每个子组件都可以以其自己独特的方式运行,因此您无法100%确定它将呈现相同的结果给定的国家 . 正如我所说,这些应该用作容器组件

  • 265

    您如何决定,您如何根据我们组件的目的/尺寸/道具/行为在这三者之间做出选择?

    使用自定义 shouldComponentUpdate 方法从 React.PureComponentReact.Component 进行扩展会影响性能 . 使用无状态功能组件是一种选择,并且没有任何开箱即用的性能优势(尚未) .

    • 对于需要易于重用的简单,仅表示的组件,更喜欢无状态功能组件 . 这样你就会产生意想不到的副作用 . 例外情况是,如果由于某种原因你有很多它们或者你真的需要优化它们的渲染方法(因为你无法为无状态功能组件定义 shouldComponentUpdate ) .

    • 扩展 PureComponent 如果你知道你的输出依赖于简单的道具/状态("simple"意味着没有嵌套的数据结构,因为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) . 根据经验,我总是尝试在应用程序处于工作状态后进行这些优化,并且已经实现了大多数功能 . 当实际出现问题时,关注性能问题要容易得多 .

    更多细节

    Functional stateless components:

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

    优点:

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

    • 理想情况下,在您的应用程序中,您应该尽可能多地拥有无状态组件,因为这通常意味着您将逻辑移到视图层之外并将其移动到redux之类的东西,这意味着您可以测试您的真实逻辑而无需渲染任何东西(更容易测试,更可重复使用等) .

    缺点:

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

    • 无法手动控制何时需要重新渲染,因为您无法定义 shouldComponentUpdate . 每次组件收到新道具时都会重新渲染(无法进行浅层比较等) . 在未来,React可以自动优化无状态组件,现在's some libraries you can use. Since stateless components are just functions, basically it'是"function memoization"的经典问题 .

    • 不支持参考:https://github.com/facebook/react/issues/4936

    A component that extends PureComponent class VS A normal component that extends Component class:

    React曾经有 PureRenderMixin 你可以附加到使用 React.createClass 语法定义的类 . mixin将简单地定义一个 shouldComponentUpdate ,在下一个道具和下一个状态之间进行浅层比较,以检查是否有任何变化 . 如果没有任何变化,则无需执行重新渲染 .

    如果要使用ES6语法,则不能使用mixins . 所以为了方便起见,React引入了一个可以继承的 PureComponent 类,而不是使用 Component . PureComponent 以与 PureRendererMixin 相同的方式实现 shouldComponentUpdate . 它必须自己实现它,因为当前/下一个状态和道具之间的浅层比较可能是最常见的场景,可以给你一些快速的性能胜利 .

    例:

    class UserAvatar extends Component {
        render() {
           return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
        }
    }
    

    如您所见,输出取决于 props.imageUrlprops.username . 如果在父组件中使用相同的道具呈现 <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> ,则React每次都会调用 render ,即使输出完全相同 . 请记住,React实现了dom diffing,因此DOM实际上不会更新 . 仍然,执行dom diffing可能很昂贵,因此在这种情况下这将是一种浪费 .

    如果 UserAvatar 组件扩展 PureComponent ,则执行浅比较 . 因为props和nextProps是相同的,所以根本不会调用 render .

    Notes on the definition of "pure" in React:

    通常,"pure function"是一个函数,它在给定相同输入的情况下始终评估相同的结果 . 输出(对于React,即 render 方法返回的内容)不会产生任何副作用(在函数外部更改"world"的操作) .

    在React中,如果您调用"stateless"一个从不调用 this.setState 并且不使用 this.state 的组件,则无状态组件不一定是上面定义的纯组件 .

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

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

    "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>
        }
    }
    

    最后我会说“哑巴”,“无国籍”和“纯粹”是完全不同的概念,有时可以重叠,但不一定,主要取决于你的用例 .

相关问题