解决Vue 使用Watch侦听器监听对象如何取得旧值问题

解决Vue 使用Watch侦听器监听对象如何取得旧值问题

Created by @一个前端er 2020/08/25

最近在项目中因为需要深度监听数组对象的变化,其实是一组表格数据,数组中的每一项都是一个对象,或者说都是一行数据,监听它的改变就必须使用Watch侦听器来完成了,并且由于对象(数组)不止一层引用,需要在Watch中开始深度监听deep:true,基本语法如下:

watch:{
    
    
  someVal:{
    
    
  handler(newVal,oldVal){
    
    
  //处理逻辑
  console.log(newVal.oldVal)
  },
   deep:true
 }
}

当数据改变之,确实是执行了handler里面的代码,但是在控制台查看新值和老值,发现它们两都是改变之后的数据,这就让我有点纳闷了,以为是哪里出现问题了,然后我就判断了它们两是不是同一个对象:

console.log(newVal === oldVal) //true

结果返回的是true,果然它们两指向的是同一个引用,这说明 Watch 只是侦听到它改变了,并没有把之前的老值给缓存下来,引用类型直接指向了引用地址。后来在网上搜了一下解决方案,基本都是类似的:使用Computed计算属性缓存 深拷贝后的 dataprops 里面的数据,然后再使用watch进行侦听,这样前前后后指向的是不同的两个对象,从而获得改变之前的旧数:

computed:{
    
    
 cacheSomeVal(){
    
    
 return deepClone(this.someVal) //JSON.parse(JSON.stringify(this.someVal)
 }
},
watch:{
    
    
  cacheSomeVal:{
    
    
  handler(newVal,oldVal){
    
    
  //处理逻辑
  console.log(newVal.oldVal)
  },
   deep:true
 }
}

但是网上基本上使用的是JSON的深拷贝,通过序列化和反序列化来进行深拷贝,但是基本不建议使用这种方式来进行深拷贝,容易带来性能问题,而且也会丢失原有的原型链,并且碰到一些特殊值如 Undefined会报错,会NaN 序列化成null等等问题,在这里推荐大家使用深拷贝算法进行深拷贝,如果是不涉及原型链、Map 或者Set等特殊的类型,只是Array|普通Object|基本数据类型(不包含Symbol)等普通对象,完全可以用以下算法进行深拷贝:

转载自25行实现 javascript 深拷贝算法(纯手写)

function isArray(value) {
    
    
  return Object.prototype.toString.call(value) === '[object Array]';
}
function isObject(value) {
    
    
  return Object.prototype.toString.call(value) === '[object Object]';
}
function deepClone(source) {
    
    
  let target = null;
  if (!isArray(source) && !isObject(source)) {
    
    
    target = source;
  }
  if (isArray(source)) {
    
    
    target = [];
    for (let i = 0; i < source.length; i++) {
    
    
      target[i] = deepClone(source[i]);
    }
  }
  if (isObject(source)) {
    
    
    target = {
    
    };
    for (let key in source) {
    
    
      target[key] = deepClone(source[key]);
    }
  }
  return target;
}

使用JSON的Api进行深拷贝基本要比深拷贝算法慢不少
在这里插入图片描述

当然大家也可以用其他的深拷贝算法,在这里我简单使用了知乎一位老哥的算法,当涉及到原型链这种比较复杂的拷贝的时候,还是推荐大家找一下全面的深拷贝算法。

关于JSON序列化与反序列的性能问题,大家可以参考这几篇文章:
为何不推荐使用JSON.stringify做深拷贝
如何提升JSON.stringify()的性能?

贴上比较的代码:

function isArray(value) {
    
    
  return Object.prototype.toString.call(value) === '[object Array]';
}
function isObject(value) {
    
    
  return Object.prototype.toString.call(value) === '[object Object]';
}
function deepClone(source) {
    
    
  let target = null;
  if (!isArray(source) && !isObject(source)) {
    
    
    target = source;
  }
  if (isArray(source)) {
    
    
    target = [];
    for (let i = 0; i < source.length; i++) {
    
    
      target[i] = deepClone(source[i]);
    }
  }
  if (isObject(source)) {
    
    
    target = {
    
    };
    for (let key in source) {
    
    
      target[key] = deepClone(source[key]);
    }
  }
  return target;
}
const a = {
    
    
  name:'a front-end developer',
  info:{
    
    
    sex:'male',
    handsome:true
  }
}
console.time();
for (let index = 0; index < 100000; index++) {
    
    
 deepClone(a)
}
console.timeEnd();


console.time();
for (let index = 0; index < 100000; index++) {
    
    
JSON.parse(JSON.stringify(a))
}
console.timeEnd();

猜你喜欢

转载自blog.csdn.net/qq_41777791/article/details/108213187