逻辑代码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]
获取到的是代理对象),因此能触发响应式。
想寻求一个解决方案,如果确实需要在对象内部修改其属性值,怎么才能触发响应式?
https://github.com/vuejs/core/issues/3743#issuecomment-835802036
https://stackoverflow.com/questions/43431550
<script setup lang="ts">import { ref } from "vue";class User { status: string; constructor() { this.status = "A"; return (async () => { this.status = "B"; // Constructors return `this` implicitly, but this is an IIFE, so // return `this` explicitly (else we'd return an empty object). return this; })(); }}const user = ref();(async function () { user.value = await new User();})();</script><template> <span v-if="user">status: {{ user.status }}</span></template>
因为在 constructor
中,你的 runAsync
函数执行时 this
指向的是被创建的源 User
实例,而不是使用 reactive
创建的被 Proxy
代理的 User
实例。
把你的 runAsync()
执行放到 User
实例化之后去执行,而不是在 constructor
中直接执行。
const add = () => { const user = reactive(new User("user1")); user.runAsync() group.addUser(user);};
你可以对比两次执行 runAsync
函数时,控制台输出的 this
查看到两次执行的 this 不同指出。
深入响应式系统 | Vue.js
javascript - Vue 3 reactivity not triggered from inside a class instance - Stack Overflow
原因跟你理解的一样。在下面例子中执行target.add()
和执行proxy.add()
效果是不一样的,因为target不是代理数据(类比你的new User
),运行target.add()
,里面的this指向target,是在对一个正常对象进行操作(你的this.runAsync()
也是这个原理)。但是执行proxy.add()
就不一样,里面的this指向proxy,是对代理数据进行操作,回到vue中就是可以出发响应式。所以可以把this.runAsync()
挪出来,通过user.runAsync()
执行。或者看有啥办法可以在constructor中把this变成响应式数据。
var target = { a: 1, add: function(){ this.a++ console.log(this.a) }}var proxy = new Proxy(target, { get: function (target, propKey, receiver) { console.log(`getting ${propKey}!`); return Reflect.get(target, propKey, receiver); }, set: function (target, propKey, value, receiver) { console.log(`setting ${propKey}!`); return Reflect.set(target, propKey, value, receiver); }});
在vue3中,以上代码定义一个class Foo,使用类属性value包装了Ref类型的成员ref。在template中,一个button被用于显示并修改Foo实例foo的value的值。请问隔了一层get/set,点击button间接修改ref时,仍能触发响应式以更新视图吗? 答案为能。why?
问题: 我使用 pinia 进行状态管理,写法大概如下,由于我修改的是数组中某个对象下的对象的属性,数组更新后,页面无法重新渲染。 当我点击按钮修改元素的 x,y,width,height 某个样式值的时候,layerList 中对应的元素的下的style对象的下属性值也会更新,由于页面是根据这个对象来渲染内联样式的, style 在手动更新后一直无法更新,想请教下各位大佬这是什么原因? 数组 l
如题,定义一个组件,设置 props 如下: 当我直接定义 的时候,会提示这样会失去响应性 所以想问下,如何可以将这样的定义,转为响应性
vue3怎么深拷贝ref和reactive对象让其保持 响应式属性? 因为用json.parse(json.stringify())的话会使其失去响应式
但这不可能访问B。
我有一个类包含如下所示同伴对象。 我想在运行时修改同伴对象的属性。我将在这个类中添加数百个属性。所以我要做动态。我使用了这个方法,得到如下错误。