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

预期的行为,但描述可能不够详细并导致一些混淆。

根据toRaw docs

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 函数的原始对象 而没有 代理。这意味着该对象是同一个对象(示例中的 og)。如果您使用 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 反应式或只读代理的原始原始对象,即 groupnewGroup 是仍然是同一个对象,区别在于 group 不会像 newGroup 那样触发 DOM 更新。


示例,UI在您更新newGroup时更新,但更新group不会触发DOM改变。然而,这两个对象仍然具有相同的引用,这就是 group.namenewGroup.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>