react性能优化:提取子组件中额外的事件绑定
我们经常在快要完成一个react
中型应用时,发现页面并没有想象中那么流畅。 react 理论性能是不错的,但是对开发者要求也很高,一些不易注意的点很容易造成性能问题。正所谓“烂泥扶不上墙”。
这是React中的常见情况:您正在映射数组,您需要每个项目调用点击处理程序和一些相关数据。
这是一个例子。我正在迭代一个用户列表,并将userId
传递给第29行的deleteUser
函数。
这是Codeandbox上的一个工作示例。(真棒)
所以, 有什么问题?
我在点击处理程序中使用箭头函数。这意味着每次渲染运行时,都会分配一个新函数。在许多情况下,这不是一件多大的事。但是,如果您有子组件,即使数据没有更改,它们也将重新渲染,因为每个渲染分配一个新的函数。
底线:避免在渲染中声明箭头函数或绑定以获得最佳性能。我的团队使用这个ESLint规则帮助提醒我们这个问题。
解决方案是什么?
那么你如何在渲染中避免绑定和箭头函数呢?一个选项是提取一个子组件。在这里,我将列表项提取到UserListItem.js
:
然后,父组件的渲染变得更简单,不再需要包含箭头函数。它只是通过props
传递每个列表项的相关上下文:
这是一个重构的工作实例。
Yay or Yuck?
此模式通过消除冗余的函数分配来提高性能。所以当这种情况适用于您的组件时,它是最有用的:
- 渲染频繁被调用
- 渲染子组件非常耗时
诚然,我提取子组件也是我提到的一个额外的工作。它需要更多的移动部件和更多的代码。所以如果你没有出现性能问题,可以说这是一个过早的优化。
所以你有两个选择:允许箭头和绑定到任何地方(如果有性能问题再处理),或禁止他们获得最佳性能和一致性。
底线:我建议禁止箭头函数并在render
中绑定。下面就是为什么:
- 你必须禁用我上面建议的有用的ESLint规则来允许它。
- 禁用linting规则后,人们可能会复制此模式,并开始禁用其他linting规则。一个处例外可以很快成为规范…
General rule for code reviews:
— Cory House 🏠 (@housecor) March 8, 2017
Each line of code should be worthy of copying.
Because people will.#cleancode
所以我发现提取子组件是一个有用的模式,以避免在渲染中绑定。
其他的反例
还有些人会想要这样写:
deleteUser = id=() => {
this.setState(prevState => {
return { users: prevState.users.filter( user => user.id !== id)}
})
}
// …
onClick={this.deleteUser(user.id)}
他们认为传递参数会比其他方式更加方便。但这和第一个糟糕的例子并无太大区别,子组件同样会再次渲染。
如果有相当多类似的输入框,像这样:
<form>
Render = () =>
Input1 value=this.state.input1 onChange= update state for field1
Input2….
Input3….
官方给出的方法是给每个 input 添加 name 属性,进而获取参数。
当然也有人会想要在 html 上动文章,比如添加自定义属性 data-id=${userId}
。这当然也能实现,但是 html不应该是数据源,这应该避免。
【翻译原文】:(有改动)https://medium.freecodecamp.org/react-pattern-extract-child-components-to-avoid-binding-e3ad8310725e