逻辑代码Demo:
<script setup lang="ts">
import { UnwrapNestedRefs, reactive, watch } from "vue";
class User {
name: string;
status: string;
constructor(name: string) {
this.name = name;
this.status = "A";
// 执行一些异步逻辑,会修改自身属性值
this.runAsync();
}
runAsync() {
setTimeout(() => {
// 第一处 这里修改后页面不会更新
console.log("1s后修改user自身的属性", this);
this.status = "B";
}, 1000);
}
}
class Group {
users: UnwrapNestedRefs<User>[];
constructor() {
this.users = [];
}
addUser(user: User) {
this.users.push(user);
}
}
const group = reactive(new Group());
watch(
() => group.users,
(users) => {
console.log("users changed", JSON.stringify(users));
},
{
deep: true,
}
);
const add = () => {
const user = reactive(new User("user1"));
group.addUser(user);
};
const change = () => {
// 第二处 这里修改后页面正常更新
group.users[group.users.length - 1].status = "AA";
};
const show = () => {
console.log(JSON.stringify(group.users));
};
</script>
<template>
<p>UploaderFiles</p>
<button @click="add">Add</button>
<button @click="change">Change</button>
<button @click="show">show</button>
<div>
<div v-for="(user, index) in group.users" :key="index">
<span>{{ user.name }}</span>
<span> - </span>
<span>{{ user.status }}</span>
</div>
</div>
</template>
大概的逻辑如上,重点在于User对象创建后会异步执行一些逻辑,然后改变自身的个别属性值,期望是页面上能实时更新改变后的数据。
大概原因我也了解,因为第一处修改的原始对象自身,其自身不具备响应式,而第二处修改的是创建的代理对象(group.users[group.users.length - 1]
获取到的是代理对象),因此能触发响应式。
想寻求一个解决方案,如果确实需要在对象内部修改其属性值,怎么才能触发响应式?
原因跟你理解的一样。在下面例子中执行
target.add()
和执行proxy.add()
效果是不一样的,因为target不是代理数据(类比你的new User
),运行target.add()
,里面的this指向target,是在对一个正常对象进行操作(你的this.runAsync()
也是这个原理)。但是执行proxy.add()
就不一样,里面的this指向proxy,是对代理数据进行操作,回到vue中就是可以出发响应式。所以可以把this.runAsync()
挪出来,通过user.runAsync()
执行。或者看有啥办法可以在constructor中把this变成响应式数据。