更新 绑定数据_从观察者模式与发布-订阅模式到双向数据绑定简单实现

1.背景

平时使用过 Vue 这个前端框架的同学,对于数据绑定这个词肯定不会陌生,进一步,它与 react 有点不同的是它还有一个双向数据绑定 v-model

数据绑定的方式能够极大程度上方便我们开发,不用去进行繁琐的 DOM 操作,这也是 MVVM 框架的一个极大的优势所在。

网上阅读到其他同学对 Vue 源码解读的文章,发现大家对 Vue 的依赖收集机制解读会有些不一样的方式。有些同学解释说依赖收集使用的观察者模式,有些同学则解释说是发布/订阅模式

因为以前认为这两个名字只是不同的叫法而已也就没太在意,但偶然发现有人问这两个模式的区别,这个问题突然也引起了我的好奇心,因此也就花了些时间来比较这两个名词背后到底有什么不同。

f32b710b9f205f8e58490ea32a50b5c1.png

2.两个模式的异同

61ef4100b7393963a11819c6deead7ca.png

观察者模式与发布-订阅模式

首先直接上图。从图中可以看出,无论是观察者模式还是发布-订阅模式,它们都是一些状态依赖于某些数据,当数据发生变化,这些状态也需要进行相应的更新的模式。不同点在于,发布-订阅模式比观察者模式多了个事件中心

更细致点说,在观察者模式中,被观察者能够完全感知到观察者的存在,一旦发生变化,被观察者负责将变化通知到所有的观察者。

举个例子,假设你要出租一套房子,你在网上发布了信息,有很多人联系你。当你的房子租出去之后,你要将这个信息通知到剩下的其他人,让他们去租别的房子。

135e6309a6385603da26c82c2dd779b6.png

而在发布-订阅模式中,因为有个事件中心的存在,当发布者发布新的信息之后,事件中心会去通知更新。

还是出租房子的例子,你把你要出租房子的信息告诉中介,同时中介那边有很多客户让中介帮忙找房,当你的房子租出去后,你只需要告诉中介房源没了,中介则会去通知意向客户房源缺失的信息。

在该模式中,订阅者向事件中心订阅某些事件,发布者并不知道有没有订阅者,只要发生变化,发布者就会通知事件中心。

下面就用实际的代码来还原一下这两个模式。

3.观察者模式

ObserverList.js

观察者列表类,由被观察者管理,添加或移除对应的观察者。

b670dc8f2df1015e015488f6c3eccd44.png
Observer.js

观察者类,接到更新通知后,做出一些反馈。

2225c8b992299fc15406a8fa6ea27e2d.png
Observed.js

被观察者类,负责维护观察者,并且将更新通知到所有观察者。

4283626a7d9c3e63a2939b7424bb5d60.png
index.js
6f2f3c08dd5c1941097e4801099c49a0.png

4.发布-订阅模式

EventCenter.js

事件中心类,包括处理订阅者的订阅/退订事件,以及发布者发布事件后的变更通知。

17911eb7c0f46fa98d7fef1af2978f79.png
Publisher.js

发布者类,负责向事件中心发布变更。

d315dac21e52c6734fefac8f1fdd9c28.png
Subscriber.js

订阅者类,对变更通知做出反馈。

5417fe78733c3ea76c2b1c4d2d946b08.png
index.js
60a50cbdf3e83623fa283d10febd1e79.png

5.小结

总的来说,两种设计模式都是为了解耦,其中发布-订阅模式解耦度更高。也可以认为发布-订阅模式是观察者模式的进一步解耦,这也是有时候会认为这两个模式一样。

现在反过来看 Vue 中的依赖搜集,它更多的倾向于观察者模式,因为对于某个数据的观察,拥有该数据的对象都能清楚的感知,当数据变化后,它都要通知到各个依赖对象。

有人认为观察者模式更多的是同步操作,而发布-订阅模式更适合异步操作(引入消息队列),但就 Vue 来说,它在派发更新过程中,也引入了队列的概念,Vue 并不是每当数据更新就立马响应,而是放入一个队列,在下一个 Tick 中再将该队列整个刷新。

6.用发布-订阅模式实现数据绑定

既然前面已经知道 Vue 是通过观察者模式来实现依赖收集的,那这里就用发布-订阅模式来简单实现一下数据绑定的功能!

8d5f49cd03316bf38dc976ecabcd4ea1.png

说干就干!

先让我们来分析一下做这个例子的流程。

1、首先,我们需要一个事件中心eventCenter,能够满足 DOM 节点对响应式数据变化的订阅,能够在数据变化后发布信息给 DOM 节点。

2、其次,我们需要对所有的响应式数据进行拦截,用Object.defineProperty方法进行改写,主要是在set函数中通过事件中心发布变化。

最后,我们需要对管理的 DOM 节点进行遍历,将{{ 响应式变量 }}替换为实际数据,并且对数据进行变更订阅。

既然流程搞清楚了,那就开始撸代码吧!

Coding…

  • eventCenter.js
819b825219330918717d5da2b5e6f1cb.png
  • dataBinding.js
754800f3cfadacf1a2dc392d9255f8dc.png
  • index.html
c24901a1338966443abc4e99481003a6.png

eventCenter.js就是事件处理中心,dataBinding.js就是完成数据绑定的主要逻辑操作了。

在遍历所有 DOM 节点的时候,对于文本节点,用正则表达式判断是否引用响应式数据,如果有则进行相应的替换,对于标签节点,首先判断是不是input节点,因为需要对v-model做双向绑定操作,然后再通过递归进行子节点的筛选。

结果演示

b33f437547e20b18f71418709cf65742.gif

相较于 Vue 的实际数据绑定的实现,这个 demo 肯定是极其简略甚至粗糙的(╭(╯^╰)╮)~比如说,一个标签节点中既有普通文本,又有响应式数据,或者一个标签里有多个数据绑定,这些都没处理。

但通过这个简单的例子,加深了自己对于 Vue 数据绑定的理解还是很有意义的~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值