写点什么

【Vue2.x 源码学习】第九篇 - 对象数据变化的观测情况

用户头像
Brave
关注
发布于: 2021 年 06 月 09 日
【Vue2.x 源码学习】第九篇 - 对象数据变化的观测情况

一,前言


上篇,主要介绍了数组深层观测的实现,核心几个点如下:


最初仅对数组类型进行了原型方法重写,并未进行递归处理,所以,当时仅实现了数组的单层劫持;

通过对数组进行 observe 递归观测,实现了对数组嵌套结构的劫持(数组中嵌套数组、数组中嵌套对象)

但是,由于 observe 方法对非对象类型不进行处理,所以,数组中的值类型将不会被劫持;


与 Vue2.x 功能进行对比,目前代码仍需实现以下功能:

  • 对象中,老属性变更为对象、数组的情况 - 需对修改值进行深层观测处理

  • 对象中,新增属性的情况 - 需进行说明

  • 数组中,新增对象、数组、普通值的情况 - 需实现数组的方法重写并对修改值进行递归处理


综上,将其划分为两大部分:

  • 对象数据变化:对象中,老属性变更为对象;对象中,新增属性的情况;

  • 数组数据变化:对象中,老属性变更为数组;数组中,新增对象、数组、普通值的情况;


本篇,对象数据变化的观测情况(老属性变更为对象、新增属性的情况)

由于数组的递归劫持尚未实现,故本篇不包含对象老属性变更为数组的情况;

将“对象老属性变更为数组”和“数组数据变化的观测情况”进行合并;


20210610:TODO-思考了一下,“对象中,老属性变更为数组的情况”应该被放到本篇“对象数据变化的观测情况”一起,理由是:即便此时还没有为对象属性修改为数组做递归观测,也应该被归类为“对象数据变化”,且后续“数组数据”实现递归观测后,这个问题也会一并得到解决;


二,对象中,老属性变更为对象的观测问题

let vm = new Vue({  el: '#app',  data() {    return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]} // data 返回一个对象  }});
// 将属性 message 由普通值,变更为对象类型vm.message = { a: 100 }// 对新增对象属性进行修改vm.message.a = 200;
复制代码


// src/observe/index.js#defineReactive
function defineReactive(obj, key, value) { observe(value); Object.defineProperty(obj, key, { get() { return value; }, set(newValue) { console.log("修改了被观测属性 key = " + key + ", newValue = " + JSON.stringify(newValue)) if (newValue === value) return value = newValue; } })}
复制代码



1,vm.message = { a: 100 }

将 data 中 message 属性由初始值"Hello Vue",变更为对象类型 { a : 100 }

由于 { a : 100 } 是 data 初始化完成后的新增对象,属于对象中新增对象

而当前版本中的新增对象不会被劫持,也就不会触发视图的更新


2,vm.message.a = 200

由于对象 { a : 100 } 没有被观测,所以修改此对象中的属性时,不会触发视图的更新


三,对象中,老属性变更为对象的观测实现


为了实现新增对象的数据观测,当设置的值为对象时,将此对象继续进行深层观测即可;


// src/observe/index.js#defineReactive
/** * 给对象Obj,定义属性key,值为value * 使用Object.defineProperty重新定义data对象中的属性 * 由于Object.defineProperty性能低,所以vue2的性能瓶颈也在这里 * @param {*} obj 需要定义属性的对象 * @param {*} key 给对象定义的属性名 * @param {*} value 给对象定义的属性值 */function defineReactive(obj, key, value) { observe(value);// 递归实现深层观测 Object.defineProperty(obj, key, { get() { return value; }, set(newValue) { console.log("修改了被观测属性 key = " + key + ", newValue = " + JSON.stringify(newValue)) if (newValue === value) return // 当值被修改时,通过 observe 实现对新值的深层观测,此时,新增对象将被观测 observe(newValue); value = newValue; } })}
复制代码


输出结果:



这样,就实现了对象中新增对象的深层观测

同时,对新增对象中的属性再次进行修改时,也能够通过数据劫持实现视图更新了


四,对象中,新增属性的情况

向 message 属性的新增对象 { a:100 } 中,添加一个不存在的属性 b,是否会进行数据劫持?


let vm = new Vue({  el: '#app',  data() {    return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]}  }});
// 属性 message 由“普通值” 变更为 “对象”类型vm.message = { a: 100 }// 向新增对象中添加一个不存在的属性vm.message.b = 200;// 修改新增属性值vm.message.b = 300;
复制代码


之前新增对象时,通过 observe 方法对新增对象进行了深层观测

而此时,向新增对象中添加新属性 b 并不会被劫持,也不会触发任何为其添加数据劫持的逻辑

所以,向新增对象中添加新属性将不会被劫持;


在 Vue2.x 中,为新增对象添加新属性,也是不能实现数据劫持的;

但可以通过 vm.$set 来实现对象中新增属性的数据观测能力;


五,结尾


又成功水了一篇,还剩 12 篇


本篇,主要介绍了数组数据变化的观测情况:


  • 实现了对象老属性值变更为对象、数组时的深层观测处理;

  • 结合实现原理,说明了对象新增属性不能被观测的原因,及如何实现数据观测;


下一篇,数组数据变化的观测情况(对象中,老属性变更为数组;数组中,新增对象、数组、普通值的情况;)

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Vue2.x 源码学习】第九篇 - 对象数据变化的观测情况