Vue双向绑定原理

本文深入探讨Vue的双向绑定原理,分为逻辑和代码两部分解析。首先,通过Observer、Complie、Dependency和Watcher四个关键函数,阐述如何从View到Model以及Model到View的更新机制,涉及发布-订阅者模式和数据劫持。然后,通过代码梳理Observer、Complie、Dependency和Watcher的具体实现,帮助读者理解Vue响应式系统的工作方式。
摘要由CSDN通过智能技术生成

本文参考b站蛋老师的视频希望能从一个更为简单的角度去理解Vue的双向绑定原理,就算没看过视频,也应该从本文收获一个更为基础的理解,同时笔者还是新手,如有不对,希望指正

本文分为两块,一块是对双向绑定原理的逻辑上的梳理,接着是对代码的梳理和理解。

1.

我们把双向绑定所需的函数为4块,分别是Observer,Complie, Dependency,Watcher。

先说说他们的作用,在来谈他们之间的关联和他们是什么。

Observer的作用是将vue中data部分的数据进行监听(数据劫持)
Complie的作用是将vue中data部分的数据解析到页面上
Dependency是发布-订阅模式 中的发布者
Watcher是发布-订阅模式 中的订阅者

通过这四个函数,我们基本可以实现vue的双向绑定原理了。

既然是双向绑定原理MVVM,就有从Model(数据)到View(视图),也有从View到Model。第一部分我们先来理解如何从View到Model。我们需要依靠的就是Complie函数,他将我们的数据解析到页面上,它的逻辑如下:

  1. 使用文档碎片来临时存放DOM元素
  2. 根据view替换fragment里文本节点的内容
  3. 将文档碎片替换成DOM元素

这样我们就解决了数据到视图的更新,但是我们还希望实现一个功能,就是Model变化时,View也能更新,这里用到的就是发布-订阅者模式了,我们需要思考的内容变成了发布-订阅者模式的逻辑是什么

毫无疑问就是当Model变化时,我们希望Compile能再次被触发,再一次将数据解析到页面。那么发布者发布文章的触发条件肯定是Model变化时触发,而订阅者的内容就是Compile再次触发。对于发布者的触发我们可以写在Observer的数据监听的setter中,而订阅者的内容就实现在每次Complie。(文档碎片有多个节点,每个节点都被一次次替换成新的节点,对每个节点的处理时新建一个订阅者)

图一:
请添加图片描述
如图所示,当数据变化时,Observer会调用Dependency,接着触发Watcher,接着调用Compile进行更新。

我们还需要想如何把Watcher放进Dependency中,这里也是相对有难点的地方,这里的做法是在创建Watcher的时候就已经把Watcher放入Dependency中了。逻辑大概如下
1.我们将watcher中放入临时Dependency属性
2.调用当前watcher监控的属性,此时会触发Observer的getter,在getter中将watcher(临时Dependency属性)放入Dependency
3.删除临时Dependency

图二:
请添加图片描述

总体的思维逻辑如下
请添加图片描述
也就是说,Observer中getter的作用是将Watcher加入到Observer,也就是图二,setter的作用则是触发视图更新,也就是图一。可以说设计的很巧妙了。

然后我们说说如何从View到Model,主要是替换绑定了v-model属性的input节点的内容,分为两部分,这部分与前面讲的其实很相似
1.视图上的改为数据(与前面相同)
2.把视图上的绑定addEventListener,当值变化时改变数据。

这里不再赘述。

总体概括如下:
Vue 的响应式是通过 Object.defineProperty 对数据进行劫持,并结合发布订阅者模式实现。 Vue 利用 Object.defineProperty 创建一个 observe 来劫持监听所有的属性,把这些属性全部转为 getter 和 setter。Vue 中每个组件实例都会对应一个 watcher 实例,它会在组件渲染的过程中把使用过的数据属性通过 getter 收集为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

2.

接下来是对代码的梳理

class Vue {
   
  // 参数为对象实例 这个对象用于告知vue需要挂载到哪个元素并挂载数据
  constructor(obj_instance) {
   
    // 给实例赋值对象的data属性
    this.$data = obj_instance.data;
    // 进行数据劫持 监听对象里属性的变化
    Observer(this.$data);
    Complie(obj_instance.el, this);
  }
}

这是Observer

//数据劫持 —— 监听实例里的数据
function Observer(data_instance) {
   
  // 递归出口
  if (!data_instance || typeof data_instance !== "object") return;
  // 每次数据劫持一个对象时都创建Dependency实例 用于区分哪个对象对应哪个依赖实例和收集依赖
  const dependency = new Dependency();
  Object.keys(data_instance).forEach((key) => {
   
    // 使用defineProperty后属性里的值会被修改 需要提前保存属性的值
    let value = data_instance[key];
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值