合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
[TOC] **** ## 1 Vue的数据绑定 >[info] 数据绑定的实现分为**数据层劫持**与**UI层事件注册**。 >[info] 这里分析vue对数据层劫持的实现机制 ## 2 实现observer(数据观察者) >[info] 对observe对象的属性进程set/get操作劫持 >[info] 实现劫持后对Observer对象属性的set与get操作可以触发响应操作, >[info] 其中的操作包括UI层的更新与其他数据的刷新 ~~~ export default class Observer{ constructor(value){ ;属性value获取 this.value = value ;属性遍历 this.walk(value) } ;遍历操作 walk(value){ ;获取属性value的keys var keys = Object.keys(value) ;遍历keys keys.foreach( ;将key对应的值转换为劫持属性 key=>this.convert(key,value[key]) ) } ;转换属性为劫持属性 convert(key,val){ defineReactive(this.value,key,val) } ;属性转换操作 expot function defineReactive(obj,key,val){ ;val递归遍历 var childOb = observe(val) ;注册为劫持属性 Object.defineProperty(obj,key,val){ enumerable : true, configurable : true, get:() => val, set:newVal=>{ childOb = observe(newVal) } } } ;属性递归操作 export function observe(value,vm){ if(!value||typeof value ! = 'object'){ return } ;生成数据监视器observer return new Observer(value) } } ~~~ ## 3 实现Dep(消息订阅器) >[info] set操作会广播消息到订阅器数组的所有订阅者 >[info] 消息订阅器数组的订阅者将会进行刷新操作。 >[info] 消息订阅器中的订阅者可以是UI层也可以是数据层 ~~~ ;Dep实现 export default class Dep{ constructor(){ this.subs = [] } addSub(sub){ this.subs.push(sub) } notify(){ this.subs.forEach(sub=>sub.update()) } } ~~~ ~~~ ;注册Dep(消息订阅器)到observer(数据观察者) export function defineReactive(obj,key,val){ ;创建订阅器数组 var dep = new Dep() ;注册为劫持属性 var childOb = observe(val) Object.defineProperty(obj,key,{ enumberable:true, configurable:true, get:()=>val, set:newVal =>{ ;旧值 var value = val ;比较新旧值 if(newVal === value){ return } ;值不相等进行处理 val = newVal childOb = observe(newVal) ;广播数据更新到消息订阅者数组 dep.notify() } }) } ~~~ ## 4 实现Watcher(消息订阅者) >[info] 消息订阅者存储在消息订阅器数组中 >[info] 数据层的set操作会广播消息到订阅器数组 >[info] 订阅器数组中的订阅者会进行自我刷新操作 >[info] 消息订阅者可以是数据层也可以是UI层 ~~~ ;实现消息订阅者 export default class Watcher{ constructor(vm,expOrFn,cb){ this.cb = cb this.vm = vm this.expOrFn = expOrFn this.value = this.get() } ;消息订阅者刷新操作 update(){ this.run() } ;刷新运行过程 run(){ const value = this.get() if( vakue ! == this.value){ this.value = value this.cb.call(this.vm) } } ;获取指令表达式的值 get(){ const value = this.vm._data[this.expOrFn] return value } } ~~~ >[info] 现在需要将消息订阅者注册到消息订阅器中 >[info] 这样数据层set层广播消息到消息订阅器数组 >[info] 消息订阅者可接受消息进行相应刷新操作 >[info] 注册的关键在Object.defineProperty的get()被调用时 >[info] Watcher的get()会调用数据层的get()。 >[info] 触发get劫持,注册Watcher到Dep数组中 >[info] 为了判断是否在Watcher中进行get,需要设置一个状态位 ~~~ ;修改后的watcher export default class Watcher{ .... get(){ ;设置状态标志 Dep.target = this ;调用Object.defineProperty()注册的get劫持 const value = this.vm._data[this.expOrFn] ;还原状态标识 Dep.target = null return value } .... } ~~~ ~~~ ;修改后的definReactive() export function defineReactive(obj,key,val){ ;创建消息订阅器数组 var dep = new Dep() ;修改为劫持属性 var childOb = observe(val) Object.defineProperty(obj,key,{ enumberable : true, configurable : true, get:() => { ;检查是否watch的get,注册到Dep中 if(Dep.target){ dep.addSub(Dep.target) } return val }, set:newVal => { var value = val ;值相等直接返回 if(newVal === value){ return } ;值不相等触发刷新操作 val = newVal childOb = observe(newVal) dep.ontify() } }) } ~~~ ## 5 总结 >[info] 数据绑定实现了对数据层set与get的劫持与刷新操作关联 >[info]Observer实现对数据属性的set/get的劫持 >[info]Dep实现对set操作的消息广播数组的管理 >[info]Watcher对应特定消息的订阅者,可以实现为数据层刷新或者UI层刷新 ## 6 参考 [vue的数据绑定实现](https://segmentfault.com/a/1190000004384515)