vue双向绑定解读

vue通过使用数据劫持+发布订阅实现数据的双向绑定。

首先,先要搞懂两个问题:

  • 什么是数据劫持(Object.defineProperty)。
  • 什么是发布/订阅模式。
1、什么是数据劫持:

举个例子,现在有个obj对象,在他身上有个name属性是’vue’,像这样: var obj = { name: ‘vue’ },现在我们想通过监听obj.name的变化,当name属性发生变化时,我们可以实时得到一个反馈,那么如何来做呢, Object.defineProperty可以实现上述需求:

在这里插入图片描述

利用Object.defineProperty重写obj.name的setter,和getter函数

可以看到,通过Object.defineProperty() 监控到obj的name属性,当对name属性发生变化的时候,便会执行setter函数在控制台输出 ‘change’, 并且data.name的值为‘3’ 。

2、什么是发布订阅:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知

举个例子,A、B、C三个人都订了一期报纸,那么邮局送报纸的话,一定是在某个时间点,给他们订的这一期一次送完,这个过程就是发布-订阅(反过来说更形象订阅-发布)映射到vue中就是,我们可以有很多个div绑定了data中的name属性(订阅),当数据发生变化,将会通知所有绑定name属性的视图进行更新(发布)。

自定义事件便是一个比较简单的例子,这里不做赘述了。

明白这两个核心思想之后,我们看下整个过程:

在这里插入图片描述

那么知道了这些之后,实现双向绑定,需要做什么?

  • 入口函数,数据监听器_observer 函数,对数据对象的所有属性进行监听,如有变动拿到最新值并通知订阅者
    指令解析器_compile函数,对每个元素节点进行遍历,根据指令绑定数据。
  • Watcher函数,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

有了基本思路后,我们接下来一步步来实现。

1、入口函数:

在这里插入图片描述

入口函数不多做解释,唯一需要理解的就是watcher的用处,定义watcher池,目的是为了将多个订阅者对象存入,如果这些订阅者订阅的某个属性发生变化,则通知所有的订阅者进行视图更新。比如我们有多个div绑定了同一个name,那么我们就需要将所有绑定name的dom元素存入watcher池,如果name发生变化,则通知这些订阅者(也就是绑定name的div)进行视图更新,这个wacther大概呢,长这样

在这里插入图片描述

可以看到,绑定name的dom元素,有三个。

2、实现_observer:
上边也说了,_observer函数需要对data里边所有数据对象进行监控,这个实现起来也简单,不过有些细节需要注意:

在对data进行遍历的时候,要将data里边的每个属性先存入watcher池占个坑位,这样,我们就可以在编译模板时,把对应的绑定值直接存入。
在set中,需要通知对应绑定属性的dom元素节点,更新视图数据。

在这里插入图片描述

上述代码主要作了三件事:

1、遍历vue实例上的data对象(注意:此处没有深度遍历,vue实际上会对data进行深度遍历) 重写data上每个属性的getter和setter。

2、将每个属性的key都存入watcher池,默认为一个数组。

3、当修改data中的某一属性时,从watcher池中取出监控这个属性的数组进行遍历(也就是this._data[key])对所有绑定这个属性的元素update ( 通知视图进行更新 )。

3、实现_compile:
首先需要深度遍历dom树,将带有v-model属性和v-bind属性的dom元素拿到。
如果发现input或textarea标签绑定有v-model属性,则获取到v-model绑定的值,将push进_observer函数中data对应的的数据watcher池,并为其添加input事件。
如果发现绑定有v-bind属性的标签,直接push进_observer函数中data对应的的数据watcher池。

在这里插入图片描述

到了这一步,基本上已经写的差不多了,我们只需要一个桥梁,把_compile和_observer联系起来,就大功告成。

3、Watcher:
watcher是个独立的构造函数,在上边的实例代码中我们也能看到,每个数据的watcher池中push的都是Watcher的实例。Watcher函数需要四个参数:当前的vue实例对象,dom元素绑定data的key,当前dom元素,绑定到当前dom元素的哪个属性上。以及一个update方法,因为我们需要在new Watcher()的时候进行视图更新(因为首先页面初始化时,遍历到绑有v-bind或v-model的元素,总要进行push Watcher实例的操作,且此时也应该将data[key]赋值给当前元素,也就是说,页面初始化过程中,遇到有v-bind的元素需要将数据更新到dom元素中去)

在这里插入图片描述

现在只需要在入口函数中,执行_compile和_observer即可

在这里插入图片描述

到目前为止,我们已经可以使用了,就像下边这样

在这里插入图片描述

参考链接:

https://javascript.ruanyifeng.com/stdlib/attributes.html Object.defineProperty

https://blog.csdn.net/q1056843325/article/details/53353850 发布/订阅模式

https://github.com/DMQ/mvvm vue双向绑定实现原理

流程图来源:知乎,具体哪篇找不到了

总结:
代码主要介绍了vue对于双向绑定的实现思路,vue真正在实现双向绑定的细节还有多未涉及的,比如重写了数组的一些原型方法如:push等。

在模版编译阶段也只是作了简单的演示,主要目的还在于理解vue双向绑定的实现思路。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值