很多人在这方面的回答都会让人感到困惑,我知道这一点是因为我曾经也有过困惑。不过过了一段时间后,我就掌握了相关概念。
- 手动绑定对象/函数以便在函数内部使用状态或属性并避免与作用域相关的问题
这并不完全正确。您不需要绑定函数来使用状态或属性,而是在失去this
上下文的作用域中将函数绑定到this
。例如,在回调函数中。
class App extends React.Component {
state = {
name: "foo",
}
aFunction() {
console.log( this.state.name );
}
render() {
return <div>{this.aFunction()}</div>;
}
}
你不需要绑定你的函数,因为 this
指向你的类,你不会失去它的上下文。但如果你在回调函数中使用你的函数,比如一个按钮,你必须要绑定它:
class App extends React.Component {
state = {
name: "foo",
}
aFunction() {
console.log( this.state.name );
}
render() {
return (
<div>
<button onClick={this.aFunction}>Click</button>
</div>
);
}
}
这样做是行不通的,因为你失去了上下文。现在,你需要以某种方式恢复上下文,对吧?好的,让我们看看如何做到这一点。首先,我想在按钮回调函数中绑定它。
<button onClick={this.aFunction.bind(this)}>Click</button>
是的,这样可以工作。但是,在每次渲染中都会重新创建它。所以:
- 在构造函数中绑定对象/函数,而不是直接在渲染中绑定
是的。不要像我上面那样绑定它,在构造函数中绑定它。
如果在构造函数中执行此操作,则Webpack仅在组件首次渲染时创建新的对象/函数存储在bundle.js文件中
如果直接在render中执行,则Webpack将在每次组件渲染和重新渲染时都创建一个新的对象/函数存储在bundle.js文件中
在这里总结了我到目前为止试图解释的内容。但是,我想Webpack不是在做这个,你的App才是。
- 如果不绑定,则无法访问state或props。您必须将当前对象分配给本地变量,否则this.state或this.props为undefined
同样,如果您在类范围内使用函数,则不必将其绑定。如果您在类外部使用此函数,例如按钮回调,则必须将其绑定。这与 state
或 props
无关,而是与使用 this
有关。
绑定的第二个选项是在构造函数中使用普通函数进行绑定,第三个选项是使用箭头函数而不进行绑定。
现在,谈论箭头函数。
1. 不需要在构造函数或渲染中绑定对象/函数
是的。
- 您不需要依赖于当前对象的本地变量,即 let that = this;
是的。
- 您将不会遇到作用域问题,并且对象/函数绑定会自动进行
是的。
但我的疑问是,我听说推荐使用普通函数并在构造函数中进行绑定,而不是使用箭头函数,因为箭头函数每次组件渲染和重新渲染时都会在Webpack bundle.js中创建新的对象/函数。
像大家说的那样,这取决于您在哪里使用它们。
render() {
return (
<div>
<button onClick={() => this.aFunction()}>Click</button>
</div>
);
}
这里,它将在每次渲染时重新创建。但是如果你不需要将任何参数传递给它,你可以通过引用来使用它。
render() {
return (
<div>
<button onClick={this.aFunction}>Click</button>
</div>
);
}
这与前一个函数相同。因此,如果您在渲染方法中看到()
,则该函数会在每次渲染时被重新创建。无论是常规函数还是箭头函数都一样。如果您以某种方式调用它,则正在重新创建它。这也适用于像aFunction.bind(this)
这样在渲染中绑定的函数。我在那里看到()
。
因此,请通过引用使用函数来避免此问题。现在,重要的问题是当我们需要一些参数时会发生什么?如果您使用箭头函数传递参数,则请尝试更改逻辑。
但它真的很重要吗?就像@Eric Kim所说,仅当您确实需要优化时才是问题。这是一个普遍建议,因为我从许多人那里听到过这个建议。但就个人而言,如果它们将在每次渲染时被重新创建,我会尽量避免使用函数。但再次强调,这完全是个人意见。
如何更改逻辑?您正在映射包含项目的数组,并创建一个按钮。在此按钮中,您正在使用一个函数将项目的名称传递给另一个函数。
{
items.map( item =>
<button onClick={() => this.aFunction(item.name)}>Click</button>
)
}
这个函数将在每次渲染时为每个项目重新创建!所以,请改变你的逻辑,创建一个独立的Item
组件并映射它。将item
,aFunction
作为属性传递。然后在这个组件中使用一个处理函数来调用你的功能。
const Item = ( props ) => {
const handleClick = () => props.aFunction( props.item.name );
return (
<button onClick={handleClick}>Click</button>
);
}
在这里,您正在使用一个带有其引用的onClick
处理程序,并调用了您的真实函数。每次渲染都不会重新创建函数。但是,作为缺点,您需要编写一个单独的组件和稍微多一点的代码。
大多数情况下,您可以应用此逻辑。也许会有一些例外,谁知道呢?所以决定权在您手中。
顺便说一句,在评论中@widged提供的Medium文章是关于这个问题的著名讨论。箭头函数真的比常规函数慢吗?是的。但是到底有多慢呢?我想不是很慢。此外,这对转译代码来说是正确的。将来当它们成为本地功能时,它们将会更快。
作为个人附注。我一直在使用箭头函数,因为我喜欢它们。但是一段时间前在一次讨论中,有人说过:
当我在类中看到箭头函数时,我认为:'此函数正在被用于/调用于这个类之外'. 如果我看到一个普通函数,我理解这个函数是在类内部调用的。
我真的很喜欢这种方法,现在如果我不需要在我的类之外调用函数,我就使用一个普通函数。
this
上,并且只会被创建一次。 - Andrew Li