vue watch监听一个对象时,新值和旧值相同?

280 阅读1分钟

一、事出场景

<template>
  <div>
    <el-button @click="handleChangeId">改变id</el-button>
    <el-button @click="handleAdd">添加</el-button>
    <ul>
      <li v-for="item of params.list" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  data () {
    return {
      params: {
        id: '1',
        list: [1, 2, 3, 4]
      }
    };
  },
  watch: {
    params: {
      handler (newVal, oldVal) {
        console.log('监听params', newVal, oldVal);
        const flag = JSON.stringify(newVal) === JSON.stringify(oldVal);
        console.log(flag);
      },
      deep: true
    }
  },
  methods: {
    handleChangeId () {
      this.params.id = '2';
    },
    handleAdd () {
      const { length } = this.params.list;
      this.params.list.push(length + 1);
    },
  }
};
</script>

例子就是上面这样一个结构,当点击【改变id】或【添加】按钮时,flag是true还是false? image.png

这特么肯定是false啊,要是true就不会打印啊。。。

但其实打印的是true

二、为什么打印true?

原因是因为监听的params是一个对象,对象的引用指向同一个对象,所以新值和旧值是相同的。人家就是这么设计的

image.png

三、如何解决

应该说如何正确地使用watch去监听

    'params.id': {
      handler (newVal, oldVal) {
        console.log('监听params.id', newVal, oldVal);
      }
    },
    'params.list': {
      handler (newVal, oldVal) {
        console.log('监听params.list', newVal, oldVal);
      },
      deep: true
    }

以上,id是可以正常监听到变化的,因为它是简单数据类型。但是list还是新值和旧值一样呢,因为list是引用数据类型,它的引用指向同一个数组。。和params的原因一样

那我们可以通过computed切断list的引用关系:

  computed: {
    list () {
      return JSON.parse(JSON.stringify(this.params.list));
    }
  },
  watch: {
    list: {
      handler (newVal, oldVal) {
        console.log('监听list', newVal, oldVal);
      },
      deep: true
    }
  }

这样,你才可以知道真正的旧值是啥样。

那一开始的params的监听实际应该写成这样:

  computed: {
    newParams () {
      return JSON.parse(JSON.stringify(this.params));
    }
  },
  watch: {
    newParams: {
      handler (newVal, oldVal) {
        console.log('监听newParams', newVal, oldVal);
      },
      deep: true
    }
  },