在类中使用

在类中使用 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