Vue 3 反应不是从 class 实例内部触发的

Vue 3 reactivity not triggered from inside a class instance

代码笔:https://codepen.io/codingkiwi_/pen/XWMBRpW

假设您有 class:

class MyClass {
  constructor(){
    this.entries = ["a"];

    //=== example change triggered from INSIDE the class ===
    setTimeout(() => {
      this.entries.push("c");
    }, 1000);
  }
}

并且在一个组件中你得到一个实例 class:

const { reactive } = Vue;

const App = {
  setup() {
    const myobject = reactive(new MyClass());

    //=== example change triggered from OUTSIDE the class ===
    setTimeout(() => {
      myobject.entries.push("b");
    }, 500);
    
    return {
      myobject
    };
  }
}

DOM 中的 myobject.entries 数组将显示条目 "a""b",但不会显示 "c"

The myobject.entries array in the DOM will display the entries "a" and "b", but not "c"

这是因为 "a" 已经在数组中,因为我们使实例具有反应性,并且 "b" 的推送发生在对象外部,通过代理。

要清楚const myobject 不包含 MyClass 实例,它包含实例的 Proxy handles/wraps原始实例!它是传递给 DOM / 模板的代理。

"c" 的推送是从对象内部发生的,不是 通过代理。因此 "c" 将被推送到数组,但不会触发反应性变化。

修复:

将我们从 class 内部更改的数组显式标记为 reactive,如下所示:

class MyClass {
  constructor(){
    this.entries = reactive(["a"]);

    //=== example change triggered from INSIDE the class ===
    setTimeout(() => {
      this.entries.push("c");
    }, 1000);
  }
}

或者像文档建议的那样尝试只使用代理对象:

The best practice here is to never hold a reference to the original raw object and only work with the reactive version:

文档:https://v3.vuejs.org/guide/reactivity.html#proxy-vs-original-identity

正如另一个答案所解释的那样,reactive 创建代理对象以启用反应性。构造函数中的 this 指的是原始 MyClass 实例而不是代理,因此它不能是反应式的。

这表示代码中的问题。 reactive 考虑了 MyClass 构造函数中发生的同步操作。在构造函数中执行异步副作用是一种反模式,原因包括使用此类构造函数的代码可能产生的影响。

这可以通过以下方式解决:

class MyClass {
  constructor(){
    // synchronous stuff only
    this.entries = ["a"];
  }

  init() {
    // asynchronous stuff, probably return a promise
    setTimeout(() => {
      this.entries.push("c");
    }, 1000);
  }
}

const myobject = reactive(new MyClass());
myobject.init() // `this` is a proxy inside `init`