如何实现双向绑定MVVM
几种实现双向绑定的做法
双向绑定无非就是在单项绑定的基础上给可输入元素(input,textarea,select)加上change(input)事件,来动态修改modal和view。
- 发布者-订阅者模式(观察者模式)
- 脏值检查(angular.js)
- 数据劫持(vue.js)
发布者模式
1.我们需要一个方法来识别哪个UI元素被绑定了相应的属性
2.我们需要监视属性和UI元素的变化
3.我们需要将所有变化传播到绑定的对象和元素
思路就是:
我们可以使用自定义的data属性在HTML代码中指明绑定。所有绑定起来的JavaScript对象以及DOM元素都将“订阅”一个发布者对象。任何时候如果JavaScript对象或者一个HTML输入字段被侦测到发生了变化,我们将代理事件到发布者-订阅者模式,这会反过来将变化广播并传播到所有绑定的对象和元素。
脏值检查
Angularjs通过脏值检测的方式来观察是否有数据变更,来决定是否更新视图,最简单的方式是通过setInervel(),anguerJs只有在进行特定的方法时才会触发
angular 会对所有绑定到 UI 上的表达式做脏检查。其实,在 angular 实现内部,所有绑定表达式都被转换为 watch()。每个 scope.scope.$watch 可不会管被 watch的表达式是否跟触发脏检查的事件有关。
- DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
- XHR响应事件 ( http )
- 浏览器Location变更事件 ( location )
- Timer事件( timeout , interval )
- 执行 digest() 或 apply()
脏值检查大概就是有一个digst ,会把作用域的数据放到watch里检测,当上述动作发生时,运行digst方法来与之前的数据进行对比,如果检测到不同,将该数据标记成dirty,然后最后刷一遍数据。
数据劫持
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
思路就是:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 (接下来我们需要实现一个消息订阅器watcher,很简单,维护一个数组,用来收集订阅者,数据变动触发notify,再调用订阅者的update方法)
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 (因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将vue实例根节点的el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中)
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图 (主要做的事情是: 1、在自身实例化时往属性订阅器(dep)里面添加自己 2、自身必须有一个update()方法 3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。)
4、mvvm入口函数,整合以上三者(通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。)
仅用于个人整理,参考: