14

自从我阅读 React 0.14 的发行说明(和其他相关炒作)以来,这个问题一直在我脑海中转来转去——我是 React 的忠实粉丝,我认为无状态组件(https://facebook.github。 io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components)是一个很好的主意,无论是为了便于编写此类组件,还是为了在代码中表达这些组件的意图对于相同的道具数据,组件应该是“纯”的。

问题是:React 怎么可能优化这些无状态的组件功能,而无需全力以赴并假设 props 引用不仅是不可变的,因为它们不应该在组件内被操纵,而且它们永远不会改变在组件生命周期之外?“常规”组件(也称为有状态组件——换句话说,贯穿整个生命周期的组件;componentWillMount、getInitialState 等)具有可选的“shouldComponentUpdate”函数的原因是 React没有假设所有的 props 和 state 引用都是完全不可变的。渲染组件后,props 引用的某些属性可能会发生变化,因此相同的“props”实例稍后可能具有不同的内容。这就是为什么人们对使用完全不可变的结构有很多兴奋的部分原因,以及为什么有人说将 Om 与 React 结合使用可以提供巨大的性能提升;因为那里使用的不可变结构保证了任何对象的任何给定实例永远不会被改变,所以 shouldComponentUpdate 可以对 props 和 state 执行非常便宜的引用相等检查(http://swannodette.github.io/2013/12/17/the -future-of-javascript-mvcs/)。

我一直在尝试找到有关此的更多信息,但一无所获。如果不假设 props 数据将由不可变类型组成,我无法想象围绕无状态组件可以做出哪些性能改进也许对非不可变 props 类型进行一些初步分析,以尝试猜测“props”和“nextProps”是否代表相同的数据?

我只是想知道是否有人对此有任何内幕消息或其他一些启发性的见解。如果 React确实开始要求 props 类型是“完全不可变的”(允许引用相等比较以确认数据没有改变),那么我认为这将是一个很大的进步,但它也可能是一个很大的变化。

4

4 回答 4

3

由于您的组件只是其参数的纯函数,因此缓存它很简单。这是因为纯函数众所周知的属性,对于相同的输入,它们将始终返回相同的输出。因为它们只依赖于它们的参数,而不是一些内部或外部状态。除非您在该函数中明确引用了一些可能被解释为状态变化的外部变量。

但是,如果您的函数组件读取一些外部变量来组成返回值,则无法进行缓存,因此,这些外部变量可能会随着时间而改变,从而使缓存值过时。无论如何,这将违反纯函数,并且它们将不再是纯函数。

React v0.14 Release Candidate页面上,Ben Alpert 说:

此模式旨在鼓励创建应包含大部分应用程序的这些简单组件。将来,我们还可以通过避免不必要的检查和内存分配来针对这些组件进行性能优化

我很确定他的意思是纯功能组件的可缓存性。

这是一个非常直接的缓存实现,用于演示目的:

let componentA = (props) => {
  return <p>{ props.text }</p>;
}

let cache = {};
let cachedA = (props) => {
  let key = JSON.stringify(props); // a fast hash function can be used as well
  if( key in cache ) {
    return cache[key];
  }else {
    cache[key] = componentA(props);
    return cache[key];
  }
}

我现在能想到的纯函数组件还有其他一些好的特性:

  • 单元测试友好
  • 比基于类的组件更轻量级
  • 高度可重用,因为它们只是函数
于 2015-12-24T15:48:18.357 回答
1

避免不必要的分配

如果我理解正确的话,无状态功能组件被转换为常规组件。从来源

function StatelessComponent(Component) {
}
StatelessComponent.prototype.render = function() {
  var Component = ReactInstanceMap.get(this)._currentElement.type;
  return Component(this.props, this.context, this.updater);
};

创建无状态组件的实例时,会分配一个新对象。这个新对象具有生命周期方法,例如componentWillMountcomponentWillReceiveProps。我猜这个计划是根本不创建这些对象。不创建对象将避免不必要的分配。

避免不必要的检查

实现生命周期方法需要进行许多检查,如下所示:

if (inst.componentWillUpdate) {
  inst.componentWillUpdate(nextProps, nextState, nextContext);
}

可以假设无状态功能组件没有这些生命周期方法。这可能是文档所指的,但我不确定。

编辑

删除了关于记忆的东西,这些东西没有回答问题或解释得很好。

于 2016-01-07T03:20:43.670 回答
0

你可以使用装饰器来组合你的无状态函数组件来执行高阶优化,以确定 React 是否应该渲染这个组件。我immutable用来在道具之间执行严格的相等检查。

假设我们有这种组件:

ClickableGreeter.js

const ClickableGreeter = (props) => (
    <div onClick={(e) => props.onClick(e)}>
        {"Hello " + props.name}
    </div>
)

ClickableGreeter.propTypes = {
    onClick: React.PropTypes.func.isRequired,
    name: React.PropTypes.text.isRequired
}

export default ClickableGreeter;

如果名称没有改变,我们希望 React 不渲染它。我正在使用一个简单的装饰器,它使用immutable库来创建 props 和 nextProps 的不可变表示并执行简单的相等性检查:

pureImmutableRenderDecorator.js

import React from 'react'
import Immutable from 'immutable';

const pureComponent = (Component, propsToRemove = []) => {

    class PureComponent extends React.Component {
        constructor(props) {
            super(props);
            this.displayName = 'PureComponent';
        }
        comparator(props, nextProps, state, nextState) {
            return (
                !Immutable.is(Immutable.fromJS(props), Immutable.fromJS(nextProps)) ||
                !Immutable.is(Immutable.fromJS(state), Immutable.fromJS(nextState))
            )
        }
        removeKeysFromObject(obj, keys) {
            var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target;
        }
        shouldComponentUpdate(nextProps, nextState) {
            let propsToCompare = this.removeKeysFromObject(this.props, propsToRemove),
                nextPropsToCompare = this.removeKeysFromObject(nextProps, propsToRemove);

            return this.comparator(propsToCompare, nextPropsToCompare, this.state, nextState)
        }
        render() {
            return <Component {...this.props} {...this.state} />
        }
    }

    return PureComponent;

}

export default pureComponent;

然后,您可以通过执行以下操作创建PureClickableGreeter组件:

const PureClickableGreeter = pureComponent(ClickableGreeter, ['onClick']) 
//we do not want the 'onClick' props to be compared since it's a callback

当然,immutable在这里使用是多余的,因为它是一个简单的字符串比较,但只要你需要一些嵌套的道具,immutable就是要走的路。您还应该记住,这Immutable.fromJS()是一个繁重的操作,但如果您不需要很多道具(这通常是无状态功能组件的全部意义所在:保留尽可能多的道具以获得更好的代码拆分和可重用性)。

于 2016-04-22T11:01:45.607 回答
0

终于有答案了!我不确定这是什么版本的 React(我怀疑 0.14 没有包含这个,只是奠定了基础)但是现在 aPureComponent没有实现“shouldComponentUpdate”,因为它是由 React 自动处理的,其中:

只浅比较对象

确实意味着如果您无法通过浅层比较可靠地检测到更改,则必须小心使用此类组件,但如果可以的话,它会使它们非常高效,并且可能避免对虚拟 DOM 进行大量更改!

请参阅此处了解更多信息ReactJs.org 的“React.PureComponent”部分

于 2021-08-25T18:40:45.837 回答