大家好,很高兴又见面了,我是"高级前端?进阶?",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
1.什么是不可变性(Immutable Data)?
Immutable Data 一旦创建,就不能被更改。对 Immutable 对象的任何修改、添加或删除操作都会返回一个新的 Immutable 对象。
Immutable 实现原理是持久化数据结构(Persistent Data Structure),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。
为了避免深度拷贝(deep Copy)把所有节点都复制一遍带来的性能损耗,Immutable 使用了共享结构(Structural Sharing),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。如下动图所示:
2.Immer介绍?
2.1 什么是Immer?
Immer 简化了不可变数据结构的处理。
Immer 可用于任何需要使用不可变数据结构的上下文。 例如与 React state、React 或 Redux reducer 或配置管理相结合。 不可变数据结构允许更改检测:如果对对象的引用没有更改,则对象本身没有更改。 此外,它使克隆更加容易:不需要复制数据树的未更改部分,并且在内存中与相同状态的旧版本共享。
const updatedUser = {
...User,
// Rest展开操作User来获取新的更新后的User对象,即updatedUser
ed: {
...User.ed,
school : {
...User.ed.school,
name: 'B'
}
}
}
一般来说,这些好处可以通过确保您永远不会更改对象、数组或映射的任何属性,而是始终创建一个更改的副本来实现。 在实践中,这会导致代码编写起来非常麻烦,而且很容易意外地违反这些约束。
Immer 将通过解决这些痛点帮助您遵循不可变数据范式:
- Immer 将检测意外突变并抛出错误
- Immer 将消除对不可变对象创建深度更新时所需的典型样板代码的需要:如果没有 Immer,则需要在每个层级手动制作对象副本。 使用 Immer 时,会对draf对象进行更改,draf对象会记录更改并负责创建必要的副本,而不会影响原始对象。
- 使用 Immer 无需学习专用 API 或数据结构。
2.2 Immer如何工作?
使用 Immer,会将所有更改应用到临时草稿,它是 currentState 的代理。 完成所有更新后,Immer 将根据对草案状态的改变生成 nextState。 这意味着可以通过简单地修改数据来与数据交互,同时保留不可变数据的所有好处。
使用 Immer 就像拥有一个私人助理。 助理拿了一封信(当前状态)并给了您一份副本(草稿)以记下更改。 完成后,助手会拿走你的草稿并为你生成最终的一封信(下一个状态)。
2.3 Immer使用?
const baseState = [
// 基础、原始state
{
title: "Learn TypeScript",
done: true
},{
title: "Try Immer",
done: false
}
]
假设有上面的基本状态,需要更新第二个待办事项,并添加第三个,同时不想改变原始的 baseState,同时?避免深度克隆(以保留第一个待办事项)。
2.3.1 不使用Immer
如果没有 Immer,将不得不小心地浅复制受更改影响的状态结构的每一层。
const nextState = baseState.slice(
// 数组浅复制
nextState[1] = {
// replace element 1...
...nextState[1], // with a shallow clone of element 1
done: true // ...combined with the desired update
}
// nextState被深度拷贝,push方法是安全的
// but doing the same thing at any arbitrary time in the future would
// violate the immutability principles and introduce a bug!
nextState.push({title: "Tweet about it"})
2.3.2 使用Immer
使用 Immer,此过程更加直接。 可以利用 produce 函数,它将开始的状态作为第一个参数传递。 produce 将负责所有的复制操作,并通过冻结数据来防止未来的意外修改。
import produce from "immer"
const nextState = produce(baseState, draft => {
// 通过produce方法操作
draft[1].done = true
draft.push({title: "Tweet about it"})
})
3.什么是Mutative?
Mutative是?一个用于高效、不可变更新的 JavaScript 库,默认情况下比 Immer 快 10 倍,甚至比纯手工制作的 reducer 还要快。
4.为什么需要Mutative?
手动编写immutable更新通常很困难,且易出错。 Immer可以使用“可变”逻辑编写更简单的不可变更新。
但它的性能问题会导致运行时性能开销。 Immer 必须默认启用自动冻结(如果禁用自动冻结,性能会更差),Immer 的这种不可变状态并不常见。 跨进程、远程数据传输等场景不得不不断地冻结这些不可变数据。
Mutative还有很多的改进,比如更好的类型推断、非侵入式标记、支持更多类型的不变性、更安全等等。
5.Mutative vs Immer性能比较
测量(操作/秒)以更新 50K 数组和 1K 对象为例,具体代码查看文末资料。
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 。
操作系统:macOS 12.6,CPU:Apple M1 Max,Node.js:16.14.2
Immer 依赖于自动冻结,如果禁用自动冻结,Immer 将有巨大的性能下降,此时Mutative 有巨大的性能领先。尤其是对于大型数据结构,Mutative将有超过 50 倍的性能领先。
因此,如果使用 Immer,则必须启用自动冻结以提高性能。 Mutative 允许默认禁用自动冻结。 使用两者的默认配置,可以看到 Mutative(5,323 ops/sec)和 Immer(320 ops/sec)之间明显的性能差距。
总体而言,Mutative 在更多的性能测试场景中比 Immer 有巨大的性能领先。
6.Mutative特点和优势
- Mutation makes immutable updates
- 支持打补丁(Support apply patches)
- 可选冻结状态(Optional freezing state)
- 自定义浅拷贝(Custom shallow copy)
- 不可变和可变数据可标记(Immutable and mutable data markable)
- 用于更安全的可变数据访问的严格模式(Safer mutable data access in strict mode)
- 支持 JSON 补丁(Support for JSON patches)
- 支持reducer(Support for reducer)
7.Mutative 和 Immer 之间的区别
差别可以通过下面的图进行说明:
除了上图的差异外,Mutative 比 Immer 有更少的错误, 经过混淆和 Gzip 压缩后,Mutative大小为 4.16KB,而具有相同功能的 Immer 大小为 4.67 KB。
8.Mutative安装&使用
8.1 安装
yarn install mutative
//npm install mutative
8.2 使用
import { create } from 'mutative';
const baseState = {
foo: 'bar',
list: [{ text: 'coding' }],
};
// baseState代表基础state
const state = create(baseState, (draft) => {
draft.foo = 'foobar';
// 作为draf传递给第二个draf函数,修改后返回新的状态
draft.list.push({ text: 'learning' });
});
9.本文总结
本文主要和大家介绍下 Mutative 和 Immer ,同时做了简单的对比。因为笔者也没有在生产项目中使用过Mutative 和 Immer,所以很多探索也就浅尝辄止,但是文末的参考资料提供了大量优秀文档以供学习,如果有兴趣可以自行阅读。
参考资料
https://blog.csdn.net/JiaPeng366/article/details/106455221/
https://dev.to/unadlib/mutative-10x-faster-than-immer-2060
https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.t
https://github.com/unadlib/mutative
https://immerjs.github.io/immer/
本文暂时没有评论,来添加一个吧(●'◡'●)