Vue 日期控件(el-date-picker)回显异常及其原理分析

Vue 日期控件(el-date-picker)回显异常及其原理分析

最近在跟一个项目,项目用的element-ui + vue ,做一个新功能的时候,用element-ui的diolag作为一个弹出层,用于数据的新增以及修改,diolag中用了el-date-picker日期控件,首次选择新增完成后,不管是再次新增或者修改,重新选择日期回显都无法改变视图显示时间,但其value已经更新。

 代码如下:
<el-date-picker v-model="rentalForm.rentSd"  type="date" value-format="yyyy-MM-dd" format="yyyy-MM-dd" style="width:150px"/>
// js 部分
showRentail(row){
      if(row){
        this.rentalForm.rentSd = row.rentSd;
      }else{
        this.rentalForm.rentSd = '';
      }
      this.dialogRentalVisible = true;
      this.$nextTick(() => {
        this.$refs['rentalForm'].clearValidate();
      })
}

数据确实会回显到日期框里,界面显示如下:例如:原日期为:2020-09-29

在这里插入图片描述

但是此时我点击日期框修改日期,例如:2020-09-09,日期框里面的值不会改变,其form对应value已更新为:2020-09-09,如下:

在这里插入图片描述

而且就算在data里面定义初始值,日期框显示时间也不会改变。

查阅相关资料后,发现官网上有这么一段话:

如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。

也就是说,data中声明或者已经赋值过的对象或者对象数组时,想对象添加新的属性或者更改该属性的的值,Vue是不能检测到其属性的改变,而触发更新视图。
追究其原因:受 ES5 的限制,Vue不能检测到对象属性的添加或删除。因为 Vue.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue转换它,才能让它是响应的。
要处理这种情况,我们可以使用$set()方法,既可以新增属性,又可以触发视图更新。
$set( target , key , value)

  • target: 要更改的数据源(可以是一个对象或者数组)
  • key 要更改的具体数据 (索引)
  • value 重新赋的值

即:

if(row){
   this.$set(this.rentalForm, "rentSd", row.rentSd);
}else{
   this.$set(this.rentalForm, "rentSd", "");
}

既然$set()能触发更新,那其原理是:

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

在这里插入图片描述

看set方法源码(项目根目录vue\node_modules\vue\src\core\observer\index)

在这里插入图片描述

数组

这里直接调用了target.splice(key, 1, val),在前面我们说过调用数组对象提供的push、pop等7个方法可以导致页面重新渲染,刚好splice也是其提供的7个方法中的一种

对象
1、判断如果key本来就是对象中的一个属性,并且key不是Object原型上的属性。说明这个key本来就在对象上面已经定义过了的,直接修改值就可以了,可以自动触发响应。

在这里插入图片描述

vue是使用的Object.defineProperty给对象做了一层拦截,当触发get的时候就会进行依赖收集(这里收集的依赖还是像数组那样,理解成渲染函数),当触发set的时候就会触发依赖,导致渲染函数执行页面重新渲染。那么第一次是在哪里触发get的呢?其实是在首次加载页面渲染的时候触发的,这里会进行递归将对象的属性都依赖收集,所以我们修改对象已有属性值得时候会导致页面重新渲染

2、首先定义变量ob的值为 target.ob ,这个 ob 属性到底是什么对象呢?vue给响应式对象都加了一个 ob 属性,如果一个对象有这个 ob 属性,那么就说明这个对象是响应式对象,我们修改对象已有属性的时候就会触发页面渲染。但当这个对象是vue实例对象或者是根数据对象,就会抛出异常告警。当这个对象是非响应式的则直接赋值返回

在这里插入图片描述

3、Set最终处理响应式对象的地方,defineReactive(ob.value, key, val) 的意思是给新加的属性添加依赖,以后再直接修改这个新的属性的时候就会触发页面渲染;ob.dep.notify() 这句代码的意思是触发当前的依赖(这里的依赖依然可以理解成渲染函数),所以页面就会进行重新渲染。

在这里插入图片描述

总结 ,以上主要是解决el-date-picker组件问题过程中,通过解决方法,了解其使用工作原理。在日常工作,我们可能会遇到各种奇奇怪怪的问题,但解决后,回过头来追溯一下其出现的根源,对于自身技术的提高都是有益无害。

猜你喜欢

转载自blog.csdn.net/vipshop_fin_dev/article/details/108367899