Immer 性能
egghead.io 第5课: 在 React 中利用 Immer 的结构共享
egghead.io 第7课: 如果没有语义变化,Immer 会使用原先的数据
这是一个关于 Immer 性能的 简单 benchmark 。该测试需要 50,000 个待办事项并更新其中的 5,000 个。 Freeze 表示状态树在生成后已被冻结。这是一种开发最佳实践,因为它可以防止开发人员意外修改状态树。
上面的数字没有反映一些东西,但实际上,Immer 有时比手写的 reducer 快 得多。这样做的原因是,Immer 会检测“无操作”状态变化,如果实际上没有任何变化,则返回原始状态,这可以避免很多重新渲染。众所周知,只需应用 immer 即可解决关键性能问题。
这些测试在 Node 10.16.3 上执行。使用 yarn test:perf
在本地重现它们。
最重要的结论:
- 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 内部,您也可以通过使用 original
或 current
函数来选择退出 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)})
慢得多