原理
vue充分利用了Object.defineProperty()
方法,通过发布订阅模式,在对data
属性的读写操作,转换成get/set
方法,当数据变化时通知视图更新。
Object.defineProperty()介绍
定义:Object.defineProperty()
是直接在对象上定义一个新属性或者可以直接修改一个现有属性,并返回该对象。
let demo={}
Object.defineProperty(demo,"test",{
get(){
console.log("test属性被读取")
return "newTest"
},
set(newValue){
console.log("test属性被重置为"+newValue)
}
})
demo.test //test属性被读取 "newTest"
demo.test="test123" //test属性被重置为test123 "test123"
通过上面例子可以发现,demo对象访问test属性时候,会先访问Object.defineProperty()中get方法,修改时会访问set方法。
这样我们就可以观察到对象中属性读写的变化。
如果我们想对demo中的所有属性进行监测,需要对属性进行遍历,然后观察:
//遍历对象属性,观察值变化
function observable(obj){
if(!obj||typeof obj!=="object") return
for(let key in obj){
defineReactive (obj,key)
}
}
//单个属性监测
function defineReactive (obj,key){
Object.defineProperty(obj,key,{
get(){
console.log(`${key}属性被读取了`);
return obj[key];
},
set(newVal){
console.log(`${key}属性被修改了`);
val = newVal;
}
})
}
发布订阅模式
在数据被读或写的时候通知那些依赖该数据的视图更新了,为了方便,我们需要先将所有依赖收集起来,一旦数据发生变化,就统一通知更新。其实,这就是典型的“发布订阅者”模式,数据变化为“发布者”,依赖对象为“订阅者”。
创建消息订阅:
class Dep {
constructor(){
this.subs = [] //搜集依赖,即哪些视图需要更新
},
//增加订阅者
addSub(sub){
this.subs.push(sub);
},
//判断是否增加订阅者
depend () {
if (Dep.target) {
this.addSub(Dep.target)
}
},
//通知订阅者更新
notify(){
this.subs.forEach((sub) =>{
sub.update()
})
}
}
Dep.target = null;
将订阅方法,增加至defineReative
方法中:
function defineReactive (obj,key,val) {
let dep = new Dep();
Object.defineProperty(obj, key, {
get(){
dep.depend(); //读取属性时,增加订阅者
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
val = newVal;
console.log(`${key}属性被修改了`);
dep.notify() //数据变化通知所有订阅者
}
})
}
订阅者Watcher
class Watcher {
constructor(vm,exp,cb){
this.vm = vm; //vue实例
this.exp = exp; //是node节点的v-model或v-on:click等指令的属性值
this.cb = cb; //Watcher绑定的更新函数;
this.value = this.get(); // 初始化时,将自己添加到订阅器的操作
},
update(){
let value = this.vm.data[this.exp];
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
},
get(){
Dep.target = this; // 缓存自己
let value = this.vm.data[this.exp] // 强制执行监听器里的get函数
Dep.target = null; // 释放自己
return value;
}
}
vue实现双向绑定
- observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。
- 在vue中v-model,v-name,{{}}等都可以对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model='name’和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。
参考链接:https://www.cnblogs.com/wangjiachen666/p/9883916.html