在类中使用
在类中使用 Immer
默认情况下,Immer 并不严格处理 Plain 对象的 non-eumerable 属性,如 getters/setters,这是出于性能的考虑。如果你希望这种行为是严格的,你可以用 useStrictShallowCopy(true)来选择。
普通对象(没有原型的对象)、数组、Map 和 Set 总是由 Immer 起草的。其他所有的对象都必须使用 immerable 符号来标记自己与 Immer 兼容。当这些对象中的一个在生产者中被突变时,它的原型在两次拷贝之间被保留下来。
import { immerable } from "immer";
class Foo {
[immerable] = true; // Option 1
constructor() {
this[immerable] = true; // Option 2
}
}
Foo[immerable] = true; // Option 3
关于如何起草类的语义如下:
- 一个类的草稿是一个新的对象,但它的原型与原始对象相同。
- 当创建一个草稿时,Immer 将把所有自己的属性从基体复制到草稿中。这包括非可数和符号属性。
- 自己的 getters 将在复制过程中被调用,就像 Object.assign 那样。
- 继承的 getters 和方法将保持原样并被草案继承。
- Immer 不会调用构造函数。
- 最终的实例将以与草稿创建时相同的机制来构造。
- 只有那些同时具有 setter 的 getters 在草案中才是可写的,否则值就不能被复制回来了。
因为 Immer 会将对象自己的 getter 解构为正常的属性,所以可以使用在其字段上使用 getter/setter 陷阱的对象,像 MobX 和 Vue 那样。Immer 不支持外来的/引擎原生的对象,如 DOM 节点或缓冲器,也不支持 Map、Set 或数组的子类化,immerable 符号不能用于它们。
因此,当使用日期对象时,你应该总是创建一个新的日期实例,而不是突变一个现有的日期对象。
import { produce, immerable } from "immer";
class ChildClock {
[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++;
});
}
}
class Clock {
[immerable] = true;
childClock1;
childClock2;
constructor(hour, minute) {
this.hour = hour;
this.minute = minute;
this.childClock1 = new ChildClock(hour, minute);
this.childClock2 = new ChildClock(hour, minute);
}
get time() {
return `${this.hour}:${this.minute}`;
}
tick() {
return produce(this, (draft) => {
draft.minute++;
draft.childClock1.minute++;
});
}
}
const clock1 = new Clock(12, 10);
const clock2 = clock1.tick();
console.log("clock2 === clock1", clock2 === clock1); // false
console.log(
"clock2.childClock1 === clock1.childClock1",
clock2.childClock1 === clock1.childClock1
); // false
console.log(
"clock2.childClock1 instanceof ChildClock",
clock2.childClock1 instanceof ChildClock
); // true
console.log(
"clock2.childClock2 === clock1.childClock2",
clock2.childClock2 === clock1.childClock2
); // true