类
普通对象(没有原型的对象)、数组、Map
和 Set
总是可以用 Immer 更新。所有其他对象都必须使用 immerable
符号将自己标记为与 Immer 兼容。当这些对象之一在 produce
中进行更改时,它的原型将保留在副本之间
import {immerable} from "immer"
class Foo {
[immerable] = true // 方式一
constructor() {
this[immerable] = true // 方式二
}
}
Foo[immerable] = true // 方式三
例子
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
语义细节
关于类的 draft
对象语义如下:
- 类的
draft
是一个新对象,但与原始对象具有相同的原型。 - 创建
draft
时,Immer 会将所有拥有的的属性从源对象复制到draft
。这包括不可枚举和符号属性。 - 源对象拥有的 getter 将在复制过程中被调用,就像
Object.assign
方法一样 - 继承的 getter 和方法将保持原样并被
draft
继承 - Immer 不会调用构造函数
- 最终实例将使用与创建
draft
相同的机制构建。 - 只有具有 setter 的 getter 才能在
draft
中写入,否则无法将值复制回来。
因为 Immer 会将对象拥有的 getter 解引用到普通属性中,所以可以使用在其字段上使用 getter/setter 获得的对象,就像MobX 和 Vue。
Immer 不支持外来/引擎原生对象,例如 DOM 节点或 Buffers,也不支持继承的 Map、Set 或数组,并且不能在它们上使用 immerable 符号。
因此,例如在使用 Date
对象时,您应该始终创建一个新的 Date
实例,而不是改变现有的 Date
对象。