Immer 数据处理流程解析
Immer 通过 produce 函数实现不可变数据的处理。整个流程可以概括为:创建 draft → 修改跟踪 → 最终化处理 → 返回新状态。下面结合流程图详细说明每个阶段的工作机制。

流程概览
从流程图中可以看到,Immer 的数据处理主要分为三个阶段:
- 初始化阶段:创建 scope 和根 draft
- 修改阶段:通过 Proxy 拦截操作,跟踪修改
- 最终化阶段:根据修改情况生成新状态
初始化阶段
当调用 produce(baseState, draft => { ... }) 时,首先会创建一个新的 scope:
const scope = enterScope(this)
const proxy = createProxy(base, undefined)
Scope 的作用:
- 管理整个 produce 调用的上下文
- 存储所有创建的 draft 对象
- 提供错误恢复机制
Draft 的创建:
- 为原始对象创建 Proxy 代理
- 每个 draft 内部都有 DRAFT_STATE 属性
- State 包含 base_(原始对象)、copy_(null)、modified_(false)等
修改阶段
当在 recipe 函数中修改 draft 时,Proxy 会拦截这些操作:
属性访问
draft.user.age = 31
访问 draft.user 时:
- 检查 user 属性是否等于原始值
- 如果相等,调用 prepareCopy(state) 准备副本
- 创建 user 对象的 draft 并返回
属性赋值
设置 age 属性时:
- prepareCopy(state):为 draft 创建 copy_(浅拷贝)
- markChanged(state):标记当前 state 为 modified_ = true
- 递归向上标记所有父级 state 为已修改
- 将新值赋给 copy_ 中的对应属性
关键设计: - 惰性复制:只有在真正修改时才创建副本 - 修改传播:子对象的修改会自动传播到父级 - 结构共享:未修改的部分保持引用共享
最终化阶段
当 recipe 函数执行完成后,进入最终化处理:
processResult(result, scope)
最终化流程
-
检查修改状态:
- 如果
modified_ = false,直接返回原始对象 - 如果
modified_ = true,处理copy_
- 如果
-
递归处理子属性:
- 遍历
copy_中的所有属性 - 如果属性是 draft,递归调用
finalize - 如果属性是普通对象,根据配置决定是否冻结
- 遍历
-
生成补丁(如果启用):
- 收集所有变更操作
- 生成
patches_和inversePatches_
-
清理资源:
- 撤销所有 draft 对象
- 释放 scope 资源
示例分析
const baseState = {
user: {
name: "John",
age: 30,
address: {
city: "New York",
country: "USA"
}
},
todos: ["task1", "task2"]
};
const nextState = produce(baseState, draft => {
draft.user.age = 31;
draft.todos.push("task3");
});
执行流程:
-
初始化:
- 创建 scope
- 创建根 draft(baseState 的代理)
-
修改
draft.user.age:- 访问
draft.user→ 创建 user draft - 设置
age→ 标记 user state 和根 state 为已修改 - 创建 user 的
copy_,修改age为 31
- 访问
-
修改
draft.todos:- 访问
draft.todos→ 创建数组 draft push操作 → 标记数组 state 和根 state 为已修改- 创建数组的
copy_,添加 “task3”
- 访问
-
最终化:
- 根 state 被修改,返回新的根对象
- user 被修改,返回新的 user 对象(age=31)
- address 未修改,保持原始引用
- todos 被修改,返回新的数组
结果:
console.log(baseState.user === nextState.user); // false
console.log(baseState.user.address === nextState.user.address); // true
console.log(baseState.todos === nextState.todos); // false
性能优化
Immer 通过以下机制优化性能:
- Copy-on-Write:只在修改时创建副本
- 结构共享:未修改的部分保持引用
- 修改跟踪:避免不必要的深度遍历
- 批量处理:scope 统一管理所有 drafts
这种设计使得 Immer 既能保证不可变性,又能在性能上接近直接修改的方式。