跳至主内容区

[非官方测试版翻译]

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

纯对象(无原型的对象)、数组、MapSet 始终会被 Immer 草稿化。其他所有对象必须使用 immerable 符号标记自身与 Immer 兼容。当这些对象在生产者函数中被修改时,其原型会在副本间保留。

import {immerable} from "immer"

class Foo {
[immerable] = true // Option 1

constructor() {
this[immerable] = true // Option 2
}
}

Foo[immerable] = true // Option 3

示例

import {immerable, produce} from "immer"

class Clock {
[immerable] = true

constructor(hour, minute) {
this.hour = hour
this.minute = minute
}

get time() {
return `${this.hour}:${this.minute}`
}

tick() {
return produce(this, draft => {
draft.minute++
})
}
}

const clock1 = new Clock(12, 10)
const clock2 = clock1.tick()
console.log(clock1.time) // 12:10
console.log(clock2.time) // 12:11
console.log(clock2 instanceof Clock) // true

详细语义

类对象的草稿化语义如下:

  1. 类的草稿是一个新对象,但保留与原始对象相同的原型。

  2. 创建草稿时,Immer 会将所有自有属性从基对象复制到草稿(严格模式下包含不可枚举属性和符号属性)。

  3. 自有 getter 会在复制过程中被调用,行为与 Object.assign 一致。

  4. 继承的 getter 和方法将保持不变并由草稿继承,因为它们存储在未被修改的原型上。

  5. Immer 不会调用构造函数。

  6. 最终实例的创建机制与草稿创建机制相同。

  7. 仅当 getter 同时具有 setter 时,该属性在草稿中才可写,否则无法回写值。

由于 Immer 会将对象的自有 getter 解引用为普通属性,因此可以处理在字段上使用 getter/setter 陷阱的对象(如 MobX 和 Vue 的响应式对象)。

注意:出于性能考虑,默认情况下 Immer 不严格处理对象的不可枚举属性(如 getter/setter)。如需严格处理,可通过 useStrictShallowCopy(config) 启用。使用 true 始终严格复制,或 "class_only" 仅对类实例严格复制(普通对象仍使用更快的宽松复制)。默认值为 false。(请记住:无论严格模式如何,自有 getter/setter 始终按值复制。目前没有按描述符原样复制的配置选项,欢迎提交功能需求或 PR)。

Immer 不支持原生特殊对象(如 DOM 节点或 Buffer),也不支持 Map/Set/数组的子类化,且 immerable 符号不能用于这些对象。

因此,处理如 Date 对象时,应始终创建新的 Date 实例而非修改现有 Date 实例。