[vue]v-model綁定computed後set卻沒有反應

遇到了v-model綁定computed沒有反應的另一種做法

情境

最近在操作computed的時候,遇到一些很奇怪的狀況,而且官網也沒有特別說明,這邊主要是說明一下我遇到的坑在哪邊,我換成用什麼方式來實做的過程

首先我用一個很簡單的例子來示例這次我遇到的狀況,當我在父元件丟一個對象到子組件,為了不要讓子組件直接改這個props的時候,因為對象有reference的特性會有two way binding的效果,所以我用spread的方式來做出一個新的對象,先看一下程式碼示例。

<template>
  <div>
    <computed-example :data.sync="obj"></computed-example>
  </div>
</template>

<script>
import ComputedExample from './ComputedExample.vue'

export default {
  name: 'parent',
  components: {
    ComputedExample
  },
  data () {
    return {
      obj: {
        one: 1
      }
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

<template>
  <div>
    <input type="number" v-model.number="localData.one">
    <input type="button" @click="submit" value="submit">
  </div>
</template>

<script>
export default {
  name: 'computedExample',
  props: [
    'data'
  ],
  computed: {
    localData: {
      get () {
        return this.data
      }
    }
  },
  methods: {
    submit () {
      console.log(this.localData)
    }
  }
}
</script>

<style>

</style>

原本會有two way binding的效果,可以看圖下示例

為了不要有two way binding導致的混亂狀況,所以我用spread來做一個pure object出來,結果原本點了值就馬上變的狀況,變成點完要離開焦點了,值才會變,感覺已經變得有點奇怪了

<template>
  <div>
    <input type="number" v-model.number="localData.one">
    <input type="button" @click="submit" value="submit">
  </div>
</template>

<script>
export default {
  name: 'computedExample',
  props: [
    'data'
  ],
  computed: {
    localData: {
      get () {
        return { ...this.data } // 改成pure object
      }
    }
  },
  methods: {
    submit () {
      console.log(this.localData)
    }
  }
}
</script>

<style>

</style>

結果

接著因為我要改值的時候,再emit改回父元件的data,所以我就為computed加了set,卻發現當值改變的時候,完全沒有發生任何事情??

<template>
  <div>
    <input type="number" v-model.number="localData.one">
    <input type="button" @click="submit" value="submit">
  </div>
</template>

<script>
export default {
  name: 'computedExample',
  props: [
    'data'
  ],
  computed: {
    localData: {
      get () {
        return { ...this.data }
      },
      // 加了set的部份
      set (value) {
        console.log(value)
        this.$emit('update:data', value)
      }
    }
  },
  methods: {
    submit () {
      console.log(this.localData)
    }
  }
}
</script>

<style>

</style>

結果可以明顯看得出來,console.log沒有印出來,也沒有改回父元件的值啊

試著不用對象只用數值的話看看是否會正常

真的讓我想不透,所以我就把整個程式碼改成用data的方式,然後按submit的時候,再emit回去了,程式碼示例如下

<template>
  <div>
    <input type="number" v-model.number="localData.one">
    <input type="button" @click="submit" value="submit">
  </div>
</template>

<script>
export default {
  name: 'computedExample',
  props: [
    'data'
  ],
  data () {
    return {
      localData: { ...this.data }
    }
  },
  methods: {
    submit () {
      this.$emit('update:data', this.localData)
    }
  }
}
</script>

<style>

</style>

結果

結論

雖然最後我改了寫法,也達成我的目的,但是我還是有點好奇為何使用對象,set會沒有反應,畢竟官方網站也沒有說到這方面的議題,而且以我的想法,我的屬性有任何改變,應該都要觸發set才對,如果有任何讀者知道原因的話,請告知我一下,感激不盡。