写在之前:以下均是个人浅见,仅供参考,会不断补充内容。
双向绑定的API
Vue2
采用数据劫持结合发布者-订阅者模式的方式,通过ES5的 Object.defineProperty() 来劫持各个属性的 setter/getter, 在数据变动时发布消息给订阅者,触发相应的监听回调。
1、Object.defineProperty(obj, prop, descriptor) ,语法内有三个参数,分别为 obj(要定义其上属性的对象);prop(要定义或修改的属性);descriptor(具体的改变方法)
2、简言之,就是用这个方法来定义一个值:
当调用时我们使用了它里面的get方法;当给这个属性赋值时,就调用了它里面的set方法;
// 基础使用
var obj = {}
Object.defineProperty(obj,'prototypeName',{
get: function() {
console.log("调用了get")
},
set: function(newValue) {
console.log("调用了set,新值是" + newValue)
}
})
obj.prototypeName // 调用了get
obj.prototypeName = 'hello' // 调用了set,新值是hello
/**
* 封装观察者(可对比下面的Proxy)
* @param {*} obj 对象
* @param {*} callback 回调函数
*/
function observe(obj, callback) {
const newObj = {}
Object.keys(obj).forEach(key => {
Object.defineProperty(newObj, key, {
configurable: true,
enumerable: true,
get() {
return obj[key]
},
// 当属性的值被修改时,会调用set,这时候就可以在set里面调用回调函数
set(newVal) {
obj[key] = newVal
callback(key, newVal)
}
})
})
return newObj
}
const obj = observe(
{
name: '子君',
sex: '男'
},
(key, value) => {
console.log(`属性[${key}]的值被修改为[${value}]`)
}
)
// 这段代码执行后,输出 属性[name]的值被修改为[子兰]
obj.name = '子兰'
// 这段代码执行后,输出 属性[sex]的值被修改为[女]
obj.sex= '女'
Vue3
采用的是ES6的Proxy API
Proxy是ES6的一个新特性,是在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
function observe(obj, callback) {
return new Proxy(obj, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value
callback(key, value)
}
})
}
const obj = observe(
{
name: '子君',
sex: '男'
},
(key, value) => {
console.log(`属性[${key}]的值被修改为[${value}]`)
}
)
// 这段代码执行后,输出 属性[name]的值被修改为[子兰]
obj.name = '子兰'
// 这段代码执行后,输出 属性[sex]的值被修改为[女]
obj.sex= '女'
重点来了
obj.age = 18
使用 Object.defineProperty 无法监听到此新增属性 age(而且同时无法监听数组和对象的变化),但是使用 Proxy 是可以监听到的。
对比上面两段代码可以发现有以下几点不同:
- Object.defineProperty 监听的是对象的每一个属性,而 Proxy 监听的是对象自身
- 使用 Object.defineProperty 需要遍历对象的每一个属性,对于性能会有一定的影响
- Proxy 对新增的属性也能监听到,但 Object.defineProperty 无法监听到
发布-订阅者模式
参考:发布-订阅者模式
简单理解:比如一个商店,会员粉丝经常要询问有什么活动,员工每次都要做一遍解答,工作重复又麻烦,但是把所有会员的邮箱收集成名单,有活动时统一发送邮件。会员就是订阅者,商店就是发布者会在合适的时候遍历名单,依次给会员发布消息。发送邮件通知就是一个的发布—订阅模式。
多个订阅者(一般是注册的函数)同时监听同一个数据对象,当这个数据对象发生变化的时候会执行一个发布事件,通过这个发布事件会通知到所有的订阅者,使它们能够自己改变对数据对象依赖的部分状态。