javascript基础修炼(9)——MVVM中双向数据绑定的基本原理 (3)

观察者类实例化时,传入一个待观察的数据对象,构造器调用遍历方法来改写数据集中每一个键的get/set方法,在读取某个键的值时,将订阅者监听器(细节下一节讲)添加进回调队列,当set改变数据集中某个键的值时,调用观察者的notify( )方法找到对应键的回调队列并以此触发。

上面的代码可以应付一般情况,但存在一些明显的问题就是集中式的回调队列管理,subQueue实际上是一个HashMap结构:

subQueue = { 'myname':[fn1, fn2, fn3], 'otherAttr':[fn11,fn12, fn13], //... }

不难看出这种管理回调的方式存在很多问题,遇到嵌套或重名结构就会出现覆盖,这个时候就不难理解Vue2.0源码中的做法了,在进行数据劫持时生成一个Dep实例,实例中维护一个回调队列用来管理发布订阅,当数据模型中的属性被set修改时,调用dep.notify( )方法来依次调用订阅者添加的回调,当属性被读取而触发get方法时,向dep实例中添加订阅者的回调函数即可。

2.2.6 发布订阅的连接

截止目前为止,还有最后一个问题需要处理,就是订阅者实例sub和发布订阅管理器实例dep存在于两个不同的作用域里,那么要怎么通过调用dep.addSub(sub)来实现订阅动作呢?换个问法或许你就发现这个问题其实并不难回答,在SPA框架中,兄弟组件之间如何通信呢?通常都是借助数据上浮(公用数据提升到共同的父级组件中)或者EventBus来实现的。

这里的做法是一致的,在策略类中某个指令对应的处理方法中,当我们准备从数据模型this.data中读取对应的初值前,先将订阅者实例sub挂载到一个更高的层级(附件的demo中简单粗暴地挂载到全局,Vue2.0源码中挂载到Dep.target),然后再去读取this.data[expr],这个时候在expr属性被劫持的get方法中,不仅可以访问到属于自己的订阅管理器dep实例,也可以通过Dep.target访问到当前节点所对应的订阅者实例,那么完成对应的订阅逻辑就易如反掌了。

2.2.7 逻辑整合

了解了上述细节,我们整理一下思路,整体看一下数据绑定所经历的各个环节:

javascript基础修炼(9)——MVVM中双向数据绑定的基本原理

2.2.8 Demo

有关上面示例中d-model和d-click指令绑定的实现,本文不再赘述,笔者提供了包含详细注释的完整Demo,有需要的读者可以直接从附件中取用,最后Demo也会存放在我的github仓库。

2.2.9 Vue2.0中有关双向绑定的源码

了解了上述细节,可以阅读《vue的双向绑定原理及实现》来看看 Vue2.0的源代码中是如何更加规范地实现双向数据绑定的。

2.3 数据劫持绑定存在的问题

基于劫持的数据绑定方法是无法感知数组方法的,Vue2.0中使用了Hack的方法来实现对于数组元素的感知,其基本原理依旧是通过代理模式实现,在此直接给出源码Vue源码链接:

//Vue2.0中有关数组方法 const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) // hack 以下几个函数 const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ] methodsToPatch.forEach(function (method) { // 获得原生函数 const original = arrayProto[method] def(arrayMethods, method, function mutator (...args) { // 调用原生函数 const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // 触发更新 ob.dep.notify() return result }) })

大致的思路是为Array.prototype上几个原生方法设置了访问代理,并将订阅管理器的消息发布方法混入其中,实现了对特定数组方法的监控。

三. 基于Proxy的数据绑定

Vue官方已经确认3.0版本重构数据绑定代码,改为Proxy实现。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxwsd.html