解决vue中,watch监听数组时,新值和旧值一样的问题

838 阅读1分钟

一:现象

<template>
  <button @click="change">改变</button>
</template>

<script>
export default {
  data() {
    return {
      arr: [1, 2, 3]
    }
  },
  watch: {
    arr: {
      handler: function (newData, oldData) {
        console.log('change', newData, oldData)
      },
      deep: true
    }
  },
  methods: {
    change() {
      this.arr.push('1111')
    }
  }
}
</script>

<style>
</style>

期望打印出来的应该是更新后的值和更新前的值,但是实际结果如下:

image.png

可以看出打印出来的新值和旧值是一样的。这是因为:

image.png

image.png

二:解决

通过官方解释,解决办法如下:

1.使用序列化和反序列化进行深拷贝

<template>
  <button @click="change">改变</button>
</template>

<script>
export default {
  data() {
    return {
      arr: [1, 2, 3]
    }
  },
  computed: {
    // 利用计算属性+序列化和反序列化进行处理
    newArr() {
      return JSON.parse(JSON.stringify(this.arr))
    }
  },
  watch: {
    newArr: {
      handler: function (newData, oldData) {
        console.log('change', newData, oldData)
      },
      deep: true
    }
  },
  methods: {
    change() {
      this.arr.push('1111')
    }
  }
}
</script>

<style>
</style>

image.png

可以明显看出是有效果的,但是这种深拷贝方式存在一些问题,比如在数据为undefined等特殊值时会报错,将NaN序列化成null等问题。所以,我们来看第二种解决办法:

2.手写深拷贝

<template>
  <button @click="change">改变</button>
</template>

<script>
export default {
  data() {
    return {
      arr: [1, 2, 3],
      obj: undefined
    }
  },
  computed: {
    // 使用计算属性进行深拷贝
    newArr() {
      return this.deepClone(this.arr)
    }
  },
  watch: {
    newArr: {
      handler: function (newData, oldData) {
        console.log('change', newData, oldData)
      },
      deep: true
    }
  },
  methods: {
    change() {
      this.arr.push('1111')
    },
    //手写深拷贝
    deepClone(value) {
      let target = null
      if (!this.isArray(value) && !this.isObject(value)) {
        target = value
      }
      if (this.isArray(value)) {
        target = []
        for (let i = 0; i < value.length; i++) {
          target[i] = this.deepClone(value[i])
        }
      }
      if (this.isObject(value)) {
        target = {}
        for (let key in value) {
          target[key] = this.deepClone(value[key])
        }
      }
      return target
    },

    isArray(value) {
      return Object.prototype.toString.call(value) === '[object Array]'
    },
    isObject(value) {
      return Object.prototype.toString.call(value) === '[object Object]'
    }
  }
}
</script>

<style>
</style>

image.png

打印出来也是成功的。