一,前言
上篇,主要介绍了数组深层观测的实现,核心几个点如下:
最初仅对数组类型进行了原型方法重写,并未进行递归处理,所以,当时仅实现了数组的单层劫持;
通过对数组进行 observe 递归观测,实现了对数组嵌套结构的劫持(数组中嵌套数组、数组中嵌套对象)
但是,由于 observe 方法对非对象类型不进行处理,所以,数组中的值类型将不会被劫持;
与 Vue2.x 功能进行对比,目前代码仍需实现以下功能:
- 对象中,老属性变更为对象、数组的情况 - 需对修改值进行深层观测处理
- 对象中,新增属性的情况 - 需进行说明
- 数组中,新增对象、数组、普通值的情况 - 需实现数组的方法重写并对修改值进行递归处理
综上,将其划分为两大部分:
- 对象数据变化:对象中,老属性变更为对象;对象中,新增属性的情况;
- 数组数据变化:对象中,老属性变更为数组;数组中,新增对象、数组、普通值的情况;
本篇,对象数据变化的观测情况(老属性变更为对象、新增属性的情况)
由于数组的递归劫持尚未实现,故本篇不包含对象老属性变更为数组的情况;
将“对象老属性变更为数组”和“数组数据变化的观测情况”进行合并;
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 来实现对象中新增属性的数据观测能力;
五,结尾
本篇,主要介绍了数组数据变化的观测情况:
- 实现了对象老属性值变更为对象、数组时的深层观测处理;
- 结合实现原理,说明了对象新增属性不能被观测的原因,及如何实现数据观测;
下一篇,数组数据变化的观测情况(对象中,老属性变更为数组;数组中,新增对象、数组、普通值的情况;)