Immer 性能

egghead.io 第5课: 在 React 中利用 Immer 的结构共享
Hosted on egghead.io
egghead.io 第7课: 如果没有语义变化,Immer 会使用原先的数据
Hosted on egghead.io

这是一个关于 Immer 性能的 简单 benchmark 。该测试需要 50,000 个待办事项并更新其中的 5,000 个。 Freeze 表示状态树在生成后已被冻结。这是一种开发最佳实践,因为它可以防止开发人员意外修改状态树。

上面的数字没有反映一些东西,但实际上,Immer 有时比手写的 reducer 得多。这样做的原因是,Immer 会检测“无操作”状态变化,如果实际上没有任何变化,则返回原始状态,这可以避免很多重新渲染。众所周知,只需应用 immer 即可解决关键性能问题。

这些测试在 Node 10.16.3 上执行。使用 yarn test:perf 在本地重现它们。

performance.png

最重要的结论:

  • Immer with proxies 大约比手写 reducer 慢 2 到 3 倍(上面的测试用例是最坏的情况,请参阅 yarn test:perf 了解更多测试情况)。这在实践中可以忽略不计。
  • Immer 的速度大致与 ImmutableJS 一样快。但是,immutableJS + toJS 明确了后期往往需要付出的代价;将 immutableJS 对象转换回普通对象,以便将它们传递给组件或者进行序列化操作在网络中传输......(还有将从服务器接收到的数据转换为不可变 JS 的前期成本)
  • 生成 patches 不会显著减慢 immer
  • ES5 后备实现的速度大约比代理实现慢两倍,在某些情况下更糟。

性能提示#

预冻结数据#

当向 Immer producer 中的状态树添加大型数据集时(例如从 JSON 端点接收的数据),可以在首先添加的数据的根上调用 freeze(json) ,来浅冻结它。这将允许 Immer 更快地将新数据添加到树中,因为它将避免递归扫描和冻结新数据的需要。

您可以随时选择退出#

immer 在任何地方都是可选的,因此手动编写性能非常苛刻的 reducers ,并将 immer 用于所有普通的的 reducers 是非常好的。即使在 producer 内部,您也可以通过使用 originalcurrent 函数来选择退出 Immer 的某些部分逻辑,并对纯 JavaScript 对象执行一些操作。

对于昂贵的搜索操作,从原始 state 读取,而不是 draft#

Immer 会将您在 draft 中读取的任何内容也递归地转换为 draft。如果您对涉及大量读取操作的 draft 进行昂贵的无副作用操作,例如在非常大的数组中使用 find(Index) 查找索引,您可以通过首先进行搜索,并且只在知道索引后调用 produce 来加快速度。这样可以阻止 Immer 将在 draft 中搜索到的所有内容都进行转换。或者,使用 original(someDraft) 对 draft 的原始值执行搜索,这归结为同样的事情。

将 produce 拉到尽可能远的地方#

始终尝试将 produce “向上”拉动,例如 for (let x of y) produce(base, d => d.push(x))produce(base, d => { for (let x of y) ) d.push(x)}) 慢得多