vue 深度监听(watch deep) Object 新旧值一样问题

为什么新旧值一样?

export default {
  name: 'deepWatchdemo',
  data() {
    return {
      form: {}
    }
  },
  watch: {
    visible(value) {
      if (value) {
        this.init()
      }
    },
    form: {
      handler: function (newValue, oldValue) {
        // newValue 是 this.form
        // oldValue 也是 this.form
      },
      deep: true
    }
  }
}

以上代码因为新(newVal)旧值(oldValue)都是监听对象的引用,所以虽然能监听到变化,但是无法进行比较

解决方法

  • 使用计算属性(computed)
  • 使用vm.$watch
export default {
  name: 'deepWatchdemo',
  data() {
    return {
      formChangeFlag: false,
      form: {}
    }
  },
  computed: {
    formStr() {
      // 转换成json字符串
      return JSON.stringify(this.form)
    }
  },
  watch: {
    formStr: {
      handler: 'formWatchHandler',
      deep: true
    }
  },
  created() {
    this.$watch(
      function () {
        // 表达式 `JSON.stringify(this.form)` 每次得出一个不同的结果时
        // 处理函数都会被调用。
        // 这就像监听一个未被定义的计算属性
        return JSON.stringify(this.form)
      },
      function (newValue, oldValue) {
        this.formWatchHandler(newVal, oldValue)
      }
    )
  },
  methods: {
    formWatchHandler(newValue, oldValue) {
      if (newValue !== oldValue) {
        this.formChangeFlag = true
      }
      // 若担心key顺序问题可以转换成Object进行判断
      // if (!this.equalObj(JSON.parse(oldValue), JSON.parse(newValue))) {
      //   this.formChangeFlag = true
      // }
    },
    // 比较对象的key-values
    equalObj(a, b) {
      if (!a && !b) {
        return true
      } else if (!a || !b) {
        return false
      } else {
        const aProps = Object.keys(a)
        const bProps = Object.keys(b)
        if (aProps.length !== bProps.length) {
          return false
        }
        for (let i = 0; i < aProps.length; i++) {
          const propName = aProps[i]
          const propA = a[propName]
          const propB = b[propName]
          if ((typeof (propA) === 'object')) {
            return this.equalObj(propA, propB)
          } else if (propA !== propB) {
            return false
          }
        }
        return true
      }
    }
  }
}