Mutative - 一个用于高效的 immutable 数据更新的 JavaScript 库,默认情况下比 Immer 快 10 倍,甚至比 reducer 更快。
手写 immutable 数据的更新通常很麻烦且容易出错的。Immer 帮助我们用"突变"逻辑编写更简单的 immutable 更新。
但是它的性能问题导致了运行时的额外性能开销。Immer 默认情况下必须启用自动冻结功能(如果禁用自动冻结功能,性能会变得特别差),这使得 Immer 的 immutable 状态并不够通用。在跨进程、远程数据传输等场景下,我们必须不断冻结这些 immutable 数据。
还有更多可以改进的部分,比如更好的类型推导、非侵入式标记、支持更多类型的 immutability 、更安全的 immutability ,等等。
这就是创建 Mutative 的原因。
Repo: https://github.com/unadlib/mutative
测量( ops/sec )更新 50K 数组和 1K 对象的性能,越大越好(查看源码). [Mutative v0.3.2 vs Immer v9.0.18]
Naive handcrafted reducer - No Freeze x 3,713 ops/sec ±0.86% (89 runs sampled)
Mutative - No Freeze x 5,323 ops/sec ±1.69% (93 runs sampled)
Immer - No Freeze x 8 ops/sec ±0.88% (23 runs sampled)
Mutative - Freeze x 875 ops/sec ±1.20% (95 runs sampled)
Immer - Freeze x 320 ops/sec ±0.45% (92 runs sampled)
Mutative - Patches and No Freeze x 752 ops/sec ±0.16% (96 runs sampled)
Immer - Patches and No Freeze x 7 ops/sec ±1.32% (23 runs sampled)
Mutative - Patches and Freeze x 425 ops/sec ±0.33% (95 runs sampled)
Immer - Patches and Freeze x 239 ops/sec ±0.99% (89 runs sampled)
The fastest method is Mutative - No Freeze
运行yarn benchmark
来在本地重现这些基准测试数据。
OS: macOS 12.6, CPU: Apple M1 Max, Node.js: 16.14.2
Immer 依赖于自动冻结的启用,如果自动冻结被禁用,Immer 将有巨大的性能下降,而 Mutative 将有巨大的性能领先,特别是对于大数据结构,它将有超过 50 倍的性能领先。
因此,如果你使用 Immer ,你将不得不启用自动冻结以提高性能。Mutative 默认允许禁用自动冻结。在两者的默认配置下,我们可以看到 Mutative (5,323 ops/sec
)和 Immer (320 ops/sec
)之间的明显性能差距。
总的来说,在更多的性能测试场景中,Mutative 比 Immer 有着巨大的性能领先。运行yarn performance
可以在本地获得所有的性能测试结果。
- | Mutative | Immer |
---|---|---|
Custom shallow copy | ✅ | ❌ |
Strict mode | ✅ | ❌ |
No data freeze by default | ✅ | ❌ |
Non-invasive marking | ✅ | ❌ |
Complete freeze data | ✅ | ❌ |
Non-global config | ✅ | ❌ |
Support IE browser | ❌ | ✅ |
与 Immer 相比,Mutative 有更少的错误,如意外的草稿逃逸等等,查看详情。
Gzip 压缩后的 Mutative 大小为
4.16KB
。具有相同功能的 Immer 大小为4.67KB
。
yarn add mutative # npm install mutative
import { create } from "mutative";
const baseState = {
foo: "bar",
list: [{ text: "coding" }],
};
const state = create(baseState, (draft) => {
draft.foo = "foobar";
draft.list.push({ text: "learning" });
});
create(baseState, (draft) => void, options?: Options): newState
create()
的第一个参数是基本状态。Mutative 将其起草并传递给 draft 函数的参数,并执行 draft 突变,直到 draft 函数执行结束,然后 Mutative 将最终定稿状态并产生新的状态。
通过设置选项,使用create()
实现更多的高级功能,它也支持 currying 。
strict - boolean
, 默认为 false 。
禁止在严格模式下访问不可起草的值。
enablePatches - boolean
, 默认为 false 。
启用 Patches ,并返回 Patches 和 inversePatches 。
enableAutoFreeze - boolean
, 默认为 false 。
启用自动冻结,并返回冻结状态。
mark - () => ('mutable'|'immutable'|function)
设置一个标记来确定对象是否是可变的,或者一个实例是否是不可变的,它还可以返回一个浅层拷贝函数( AutoFreeze 和 Patches 都应该被禁用)。
Mutative 是一个在很大程度上依赖于使用 Proxy 对象的库,Proxy 对象是现代网络浏览器的一个重要功能,允许对对象的各种操作进行拦截。因此,Mutative 可能不完全兼容不支持 Proxy 对象的老式浏览器,如 Internet Explorer 。然而,这些旧的浏览器在整个浏览器市场中只占很小的比例,所以对兼容性的影响可能是最小的。
Mutative 优化的重点是浅拷贝优化、更完整的懒草稿、定稿过程优化等等。
是的。除非你必须与 Internet Explorer 兼容,否则 Mutative 几乎支持 Immer 的所有功能,而且你可以轻松地从 Immer 迁移到 Mutative 。
Mutative 的灵感来自 Immer 。Mutative 的目标是高效的 immutable 更新,专注于性能改进和更好的 API ,带来更好的开发体验。2018 年,Immer 被创建,但到了 2022-2023 年,我们需要一个性能更好的 immutable 更新的库来帮助我们提高应用性能。