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`
代码笔: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`