React性能:匿名函数 vs 命名函数 vs 方法

7

我想知道在React.js中,在组件中声明匿名函数、命名函数或方法之间是否存在性能差异。

具体来说,以下哪种方式更具性能优势?

class MyComponent extends React.Component {
  render() {
    return (
      <input
        type="text"
        value={foo}
        onChange={(e) => {
          this.setState({ foo: e.target.value });
        }}
      />
    );
  }
}

class MyComponent extends React.Component {
  ...
  render() {
    function handleChange(e) {
      this.setState({ foo: e.target.value });
    }
    return (
      <input
        type="text"
        value={foo}
        onChange={handleChange}
      />
    );
  }
}

class MyComponent extends React.Component {
    ...
    handleChange(e) {
      this.setState({ foo: e.target.value });
    }

    render() {
      return (
        <input
          type="text"
          value={foo}
          onChange={this.handleChange}
        />
      );
    }

}
2个回答

8

当然有,你的代码的第三个版本是在React组件的Render块里引用函数的正确方式。

为什么呢?

通常来说,对于需要被调用一次以上的方法而言,嵌套函数是一种反模式。这主要是因为JavaScript引擎将函数视为任何其他值,并且在父调用完成后必须创建并随后销毁它。

如果你需要能够从handleChange()中访问this,则需要将该方法绑定到组件的上下文中。以下是不会产生任何负面性能影响的方法:

通过构造函数使用原始ES6:

class MyComponent extends React.Component {
    constructor(props) {
        super(props)
        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(e) {
        this.setState({ foo: e.target.value });
    }

    render() {
        return (
            <input
                type="text"
                value={foo}
                onChange={this.handleChange}
            />
        )
    }
}

类属性中的箭头函数(需要babel并启用transform-class-properties插件):

class MyComponent extends React.Component {
    handleChange = (e) => {
        this.setState({ foo: e.target.value });
    }

    render() {
        return (
            <input
                type="text"
                value={foo}
                onChange={this.handleChange}
            />
        )
    }
}

装饰器类方法(需要使用babel与transform-decorators-legacy以及core-decorators):

import { autobind } from 'core-decorators'

class MyComponent extends React.Component {

    @autobind
    handleChange(e) {
        this.setState({ foo: e.target.value });
    }

    render() {
        return (
            <input
                type="text"
                value={foo}
                onChange={this.handleChange}
            />
        )
    }
}

希望这可以帮到您!

1
我希望JavaScript不那么凌乱。 - mnj

5
第三个选项是最好的。你要避免在render函数中设置状态setState({}),因为它不再是“纯”的了。这是人们在谈论函数式编程时使用的术语。它基本上意味着没有副作用。即使你没有立即在渲染函数中调用那些带有setState的函数,我认为最好还是将那些逻辑拉出来,并养成思考纯函数的习惯。
引用: 纯函数是指:
给定相同的输入,总是返回相同的输出。 不产生副作用。 不依赖于外部可变状态。
此外,考虑性能,每次渲染函数运行时都会创建一个新的函数。你只需要创建这些函数一次,所以最好将它们拉到外面。
渲染函数运行的频率如何?可能非常多!每当你改变状态或从父组件传递新的props时都会运行。

1
我想补充一下,选项1和2在每次渲染时都会创建一个新的函数,这是低效的,这也是OP所寻找的原因。 - Andrew Li
是的,你不需要重复创建这些函数。 - Turnipdabeets
所有三种渲染方法在功能纯度方面完全等效 - 它们都不是纯函数(因为使用了 this),而且 onChange 属性在所有三种情况下都包含相同类型(函数)的值...第二和第三个选项甚至没有正确绑定(如 Luigi 的答案中所示),因此比较性能有点误导性。 - Aprillion
这个答案是不正确的。正如@Aprillion所提到的,所有三个例子都是纯函数。第二个例子中,使用命名函数甚至不会为handleChange创建一个新的引用。第一个例子中的匿名函数将在每次渲染时获得一个新的引用。但我相信现代JavaScript引擎会倾向于优化掉它。 - Erik Pukinskis
谢谢您!我最近学到了这个,您为我解释得更加清晰明了。 - jefelewis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接