为什么 Vue3 中的空对象不是反应式的?

Why is empty object in Vue3 not reactive?

当我从空对象创建 ref 并稍后添加对象属性时,没有反应:

<template>
  <p>{{hello}}</p>
  <button @click="add()">Click me</button>
</template>

<script>
import {ref} from 'vue';
export default {
  name: "Test",
  setup(){
    const myData = ref({});
    return {myData}
  },
  methods: {
    add(){
      this.myData["text"] = "Hello";
      console.log(this.myData);
    }
  },
  computed: {
    hello(){
      return this.myData.hasOwnProperty("text")) ? this.myData["text"] : "no text";
    }
  }
}
</script>

单击按钮显示 myData 已更改,但计算的 属性 hello 不会更新。

也尝试了 reactive({}) 而不是 ref({}) 但没有成功。

当我们使用 const myData = ref({"text": "no text"});.

等属性初始化 ref 时,它会起作用

但是为什么空对象不行呢?

编辑:
终于找到问题的确切原因以及如何解决: Vue3 的反应性核心不是 Object.keys() 的警报,而是属性值的警报,空对象没有任何。但是,如果计算的 属性 定义为

,则可以使 Vue3 发出警报
computed: {
    hello(){
      return Object.keys(this.myData).indexOf("text") > -1 ? this.myData["text"] : "no text";
    }

调用Object.keys(this.myData)需要让反应系统知道我们感兴趣的是什么。这类似于在Object.keys(this.myData)上设置watch而不是观察this.myData.

尝试更新您的 ref 对象,例如

this.myData = {"text": "Hello"}

const { ref, computed } = Vue
const app = Vue.createApp({
  /*setup(){
    const myData = ref({});
    const hello = computed(() => myData.value.hasOwnProperty("text") ? myData.value.text : myData.value = "no text")
    const add = () => {
      if(Object.keys(myData.value).length === 0) {
        myData.value = {'text': "Hello"};
      } else {
        myData.value.otherProperty = "Hello again"
      }
    }
    return { myData, add, hello }
  },*/
  setup(){
    const myData = ref({});
    return { myData }
  },
  methods: {
    add(){
      if(Object.keys(this.myData).length === 0) {
        this.myData = {"text": "Hello"}
      } else {
        this.myData.otherProperty = "Hello again"
      }
      console.log(this.myData)
    },
  },
  computed: {
    hello(){
      return Object.keys(this.myData).length !== 0 ? this.myData[Object.keys(this.myData)[Object.keys(this.myData).length - 1]] : "no text"
    }
  }
})
app.mount('#demo')
<script src="https://unpkg.com/vue@3.2.29/dist/vue.global.prod.js"></script>
<div id="demo">
  <p>{{ hello }}</p>
  <button @click="add">Click me 2 times</button>
</div>

如果您更改要定义的计算 属性,使其在返回之前直接引用 myData['text'],则事情会按预期工作:

  computed: {
    hello() {
      return this.myData['text'] || 'no text'; // works
    }

我怀疑你的原始代码是怎么回事,Vue dependency-tracking 代码无法看到你的函数依赖于 myData。考虑在对象上存在 text 属性 之前(由 Vue)调用 hello。在那种情况下,函数 returns 在实际接触 proxied value 之前(它 short-circuits 一旦看到 hasOwnProperty 返回了 false)。

后续调用的依赖性 , so if your computed property doesn't touch any reactive variables when called, Vue doesn't see it as having any external dependencies, and so won't bother calling it in the future. It will just use the previously-cached value