一、前言
数据双向绑定作为 Vue 核心功能之一,其实现原理主要分为两部分:
- 数据劫持
- 发布订阅模式
本篇文章主要介绍 Vue 实现数据劫持的思路,下一篇则会介绍发布订阅模式的设计。
二、针对 Object 类型的劫持
对于 Object 类型,主要劫持其属性的读取与设置操作。在 JavaScript 中对象的属性主要由一个字符串类型的“名称”以及一个“属性描述符”组成,属性描述符包括以下选项:
- value: 该属性的值;
- writable: 仅当值为 true 时表示该属性可以被改变;
- get: getter (读取器);
- set: setter (设置器);
- configurable: 仅当值为 true 时,该属性可以被删除以及属性描述符可以被改变;
- enumerable: 仅当值为 true 时,该属性可以被枚举。
上述 setter 和 getter 方法就是供开发者自定义属性的读取与设置操作,而设置对象属性的描述符则少不了 Object.defineProperty() 方法:
function defineReactive (obj, key) {
let val = obj[key]
Object.defineProperty(obj, key, {
get () {
console.log(' === 收集依赖 === ')
console.log(' 当前值为:' + val)
return val
},
set (newValue) {
console.log(' === 通知变更 === ')
console.log(' 当前值为:' + newValue)
val = newValue
}
})
}
const student = {
name: 'xiaoming'
}
defineReactive(student, 'name') // 劫持 name 属性的读取和设置操作
上述代码通过 Object.defineProperty() 方法设置属性的 setter 与 getter 方法,从而达到劫持 student 对象中的 name 属性的读取和设置操作的目的。
读者可以发现,该方法每次只能设置一个属性,那么就需要遍历对象来完成其属性的配置:
Object.keys(student).forEach(key => defineReactive(student, key))
另外还必须是一个具体的属性,这也非常的致命。
假如后续需要扩展该对象,那么就必须手动为新属性设置 setter 和 getter