vue3 反应性意外行为
vue3 reactive unexpected behaviour
我有一个反应对象,在保存函数上我调用 toRaw 以消除对象反应性,但是,当我更改反应对象道具时,组对象正在改变....
怎么样???
const newGroup = reactive<Group>({ id: undefined, name: '', enabled: false })
const handleSave = () => {
const group = toRaw(newGroup)
persist(group).then(() => {
newGroup.id = undefined
newGroup.name = 'demo'
console.log(isReactive(group)) //say false but the group.name value is demo
})
}
将反应性对象解构为 const group = { ...newGroup } 看起来可行,但仍然不明白为什么 toRaw 不起作用。
编辑:
整个问题来自于在处理反应对象时缺乏控制感,例如:
以下计算的对象从商店中检索一个列表,然后在模板中将其绘制为 table 的行,单击该行会更改所选元素的状态。在函数中我被迫解构记录以避免修改在持久化之前触发启用的更改
有没有更好的办法?
我应该创建只读组吗?
//in setup()
const groups = computed <Group []> (() => getters ['registry / groups'])
const toggle = (record: Group) => {
const group = { ...record }
group.enabled = !group.enabled
persist(group)
}
//in template
<a-menu-item @ click = "toggle (record)">
isReactive(group)
returnfalse
正常。
实际上在你的代码中我们有 const group = toRaw(newGroup)
=> group
是反应值的原始对象newGroup
.
=> group
是原始值,不是反应值
如果您尝试 isReactive(newGroup)
这将 return true
这是预期的行为,但描述可能不够详细并导致一些混淆。
Returns the raw, original object of a reactive or readonly proxy. This is an escape hatch that can be used to temporarily read without incurring proxy access/tracking overhead or write without triggering changes. It is not recommended to hold a persistent reference to the original object. Use with caution.
当您使用 toRaw
时发生的事情是,您正在取回您传递给 reactive
函数的原始对象 而没有 代理。这意味着该对象是同一个对象(示例中的 o
和 g
)。如果您使用 toRaw
而不是代理本身,toRaw
只允许您“转义”绑定到对象的 reactivity/listeners。即使对象更新,也不会应用反应性(滴到 DOM)。
这是一个例子:
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g = Vue.toRaw(m);
console.log(o.name, m.name, g.name);
g.name = g.name + " Ding Dong";
console.log(o.name, m.name, g.name);
console.log("Is 'm' same as 'o'? ", m == g);
console.log("Is 'g' really just 'o'? ", o == g);
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
如果你想创建一个不会更新原始对象的对象,最简单的方法就是克隆它。
大多数时候你可以使用JSON.parse(JSON.stringify(o))
。需要注意的是,如果您有循环引用(即 {a:b, b:a}
),就会遇到问题。
另一种选择是使用解构const copy = {...o}
。这里需要注意的是它是一个浅拷贝。示例 o = {a:{b:"I will update"}, c:"I won't"}
,其中 copy.a
仍然是与 o.a
相同的对象
那看起来就像是
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g0 = {...Vue.toRaw(m)}; // destructure
let g1 = JSON.parse(JSON.stringify(Vue.toRaw(m))); // clone with json
或者,您也可以使用一些为您处理克隆对象的库。
你对反应性有误解。 反应对象将数据与 DOM 连接,而不是 javascript 对象之间,即当更新反应对象的 属性 时,DOM 自动更新。
toRaw
returns 反应式或只读代理的原始原始对象,即 group
和 newGroup
是仍然是同一个对象,区别在于 group
不会像 newGroup
那样触发 DOM 更新。
示例,UI在您更新newGroup
时更新,但更新group
不会触发DOM改变。然而,这两个对象仍然具有相同的引用,这就是 group.name
和 newGroup.name
始终相同的原因。
Vue.createApp({
setup() {
const newGroup = Vue.reactive({ id: undefined, name: '', enabled: false })
const group = Vue.toRaw(newGroup)
return {
newGroup,
group
}
}
}).mount('#app')
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<div id="app">
<button @click="newGroup.name = 'reactive update'">reactive update</button>
newGroup Name: {{newGroup.name}}<br/>
<button @click="group.name = 'raw update'">raw update</button>
group Name: {{group.name}}
</div>
我有一个反应对象,在保存函数上我调用 toRaw 以消除对象反应性,但是,当我更改反应对象道具时,组对象正在改变.... 怎么样???
const newGroup = reactive<Group>({ id: undefined, name: '', enabled: false })
const handleSave = () => {
const group = toRaw(newGroup)
persist(group).then(() => {
newGroup.id = undefined
newGroup.name = 'demo'
console.log(isReactive(group)) //say false but the group.name value is demo
})
}
将反应性对象解构为 const group = { ...newGroup } 看起来可行,但仍然不明白为什么 toRaw 不起作用。
编辑:
整个问题来自于在处理反应对象时缺乏控制感,例如:
以下计算的对象从商店中检索一个列表,然后在模板中将其绘制为 table 的行,单击该行会更改所选元素的状态。在函数中我被迫解构记录以避免修改在持久化之前触发启用的更改 有没有更好的办法? 我应该创建只读组吗?
//in setup()
const groups = computed <Group []> (() => getters ['registry / groups'])
const toggle = (record: Group) => {
const group = { ...record }
group.enabled = !group.enabled
persist(group)
}
//in template
<a-menu-item @ click = "toggle (record)">
isReactive(group)
returnfalse
正常。
实际上在你的代码中我们有 const group = toRaw(newGroup)
=> group
是反应值的原始对象newGroup
.
=> group
是原始值,不是反应值
如果您尝试 isReactive(newGroup)
这将 return true
这是预期的行为,但描述可能不够详细并导致一些混淆。
Returns the raw, original object of a reactive or readonly proxy. This is an escape hatch that can be used to temporarily read without incurring proxy access/tracking overhead or write without triggering changes. It is not recommended to hold a persistent reference to the original object. Use with caution.
当您使用 toRaw
时发生的事情是,您正在取回您传递给 reactive
函数的原始对象 而没有 代理。这意味着该对象是同一个对象(示例中的 o
和 g
)。如果您使用 toRaw
而不是代理本身,toRaw
只允许您“转义”绑定到对象的 reactivity/listeners。即使对象更新,也不会应用反应性(滴到 DOM)。
这是一个例子:
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g = Vue.toRaw(m);
console.log(o.name, m.name, g.name);
g.name = g.name + " Ding Dong";
console.log(o.name, m.name, g.name);
console.log("Is 'm' same as 'o'? ", m == g);
console.log("Is 'g' really just 'o'? ", o == g);
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
如果你想创建一个不会更新原始对象的对象,最简单的方法就是克隆它。
大多数时候你可以使用JSON.parse(JSON.stringify(o))
。需要注意的是,如果您有循环引用(即 {a:b, b:a}
),就会遇到问题。
另一种选择是使用解构const copy = {...o}
。这里需要注意的是它是一个浅拷贝。示例 o = {a:{b:"I will update"}, c:"I won't"}
,其中 copy.a
仍然是与 o.a
那看起来就像是
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g0 = {...Vue.toRaw(m)}; // destructure
let g1 = JSON.parse(JSON.stringify(Vue.toRaw(m))); // clone with json
或者,您也可以使用一些为您处理克隆对象的库。
你对反应性有误解。 反应对象将数据与 DOM 连接,而不是 javascript 对象之间,即当更新反应对象的 属性 时,DOM 自动更新。
toRaw
returns 反应式或只读代理的原始原始对象,即 group
和 newGroup
是仍然是同一个对象,区别在于 group
不会像 newGroup
那样触发 DOM 更新。
示例,UI在您更新newGroup
时更新,但更新group
不会触发DOM改变。然而,这两个对象仍然具有相同的引用,这就是 group.name
和 newGroup.name
始终相同的原因。
Vue.createApp({
setup() {
const newGroup = Vue.reactive({ id: undefined, name: '', enabled: false })
const group = Vue.toRaw(newGroup)
return {
newGroup,
group
}
}
}).mount('#app')
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<div id="app">
<button @click="newGroup.name = 'reactive update'">reactive update</button>
newGroup Name: {{newGroup.name}}<br/>
<button @click="group.name = 'raw update'">raw update</button>
group Name: {{group.name}}
</div>