首页 > 其他分享 >Vue2:怎么实现响应式双向绑定?

Vue2:怎么实现响应式双向绑定?

时间:2023-06-20 14:04:56浏览次数:39  
标签:Dep 绑定 视图 Watcher 更新 Vue2 双向 数据

输入图片描述

一、vue2怎么实现双向绑定原理

在Vue2中,双向绑定的实现是通过Vue2的响应式系统和数据绑定机制来完成的。下面是Vue2实现双向绑定的简要原理:

  1. 数据劫持:当创建Vue实例时,Vue2会对data选项中的所有属性进行数据劫持。这通过使用Object.defineProperty()方法将每个属性转换为getter和setter,并在数据被访问或修改时触发相应的操作。

  2. Watcher监听器的创建:在编译模板时,Vue2会解析模板中的指令和表达式,并创建相应的Watcher监听器。每个Watcher与一个数据关联,负责监听该数据的变化,并更新相关的视图。

  3. 依赖收集:模板编译过程中,Watcher会自动收集所依赖的数据(如data、computed、props等),建立起数据与Watcher之间的关联关系。这样,当数据发生改变时,就能找到需要更新的对应Watcher,并通知其进行更新。

  4. 发布-订阅模式:当数据发生变化时,数据劫持会触发setter函数,并调用发布-订阅模式的调度中心。调度中心会根据依赖收集得到的相关Watcher列表,依次通知这些Watcher进行更新。

  5. 视图更新:每个被通知的Watcher都会执行相应的更新操作,比如重新渲染视图或更新DOM元素。这样,数据的变化能够实时反映到视图中。

需要注意的是,Vue会将多个数据的变化收集起来,在下一个事件循环或异步任务中进行批量更新。这样可以减少不必要的重复渲染和DOM操作,提高性能。

通过数据劫持、Watcher监听器和发布-订阅模式的协同工作,Vue能够实现数据与视图的自动同步更新,并提供高效的响应式系统。

这种设计模式的优势在于解耦合、灵活性和性能优化,使得Vue能够以简洁、高效的方式处理复杂的数据变化和视图更新。

输入图片描述

二、vue2有数据劫持了,为什么还要使用Watcher监听器?

虽然Vue2的数据劫持能够检测到数据的变化,但它仅仅负责将数据属性转换为getter和setter,并在数据被访问或修改时触发相应的操作。Watcher监听器的作用是通过观察数据的变化并更新相关的视图。

以下是Watcher监听器的几个主要作用:

  1. 依赖收集:Watcher会自动收集模板中使用到的响应式数据(如data、computed、props等),建立起数据与Watcher之间的关联关系。这样,当数据发生改变时,就能找到需要更新的对应Watcher,并通知其进行更新。

  2. 视图更新:当响应式数据发生变化时,Watcher会接收到通知,并触发相应的更新操作,比如重新渲染视图或更新DOM元素。

  3. 惰性求值:Watcher还支持计算属性(computed)和侦听器(watcher),它们可以根据数据的变化动态计算和派发其他的更新。

由于Vue2使用虚拟DOM的技术,直接操作真实DOM的代价较高,因此Vue2采用异步的方式来批量处理数据变化和视图更新。而Watcher在数据变化后进行异步调度,保证了组件更新的效率和性能。

总结起来,数据劫持只是Vue2实现响应式的一部分,而Watcher监听器则负责收集依赖、触发视图更新和实现计算属性等功能。Watcher是Vue2响应式系统中的重要组成部分,它确保了数据变化能够正确地反映到视图中,从而实现了双向绑定的效果。

三、vue2有数据劫持和Watcher监听器,为什么还要发布-订阅模式?

在Vue2中,除了数据劫持和Watcher监听器之外,还使用了发布-订阅模式(Pub-Sub Pattern)来进一步优化响应式系统的效率和灵活性。

发布-订阅模式是一种解耦合的设计模式,其中有一个调度中心(通常称为事件总线或消息中心),负责管理订阅者(观察者)和发布者(被观察者)之间的通信。当发布者的状态发生变化时,它会通知调度中心,然后调度中心再将相应的消息传递给所有订阅者。

在Vue2中,发布-订阅模式用于建立数据的变化与视图更新之间的联系,以实现组件中数据和视图的同步。

以下是发布-订阅模式在Vue2中的主要作用:

  1. 批量更新:当多个数据同时发生变化时,Vue2会将这些变化收集起来,并在下一个事件循环中进行批量更新,从而减少不必要的重复渲染。

  2. 异步更新:由于视图更新可能涉及到耗时的DOM操作,Vue2采用异步更新策略,即在下一个事件循环中执行视图更新。这样可以避免频繁的DOM操作,提高性能。

  3. 懒执行:Vue2对于计算属性(computed)和侦听器(watcher)采用了懒执行的方式,只有在真正需要获取计算属性的值或监听的数据发生变化时才会进行相应的求值和派发。

  4. 分发通知:发布-订阅模式通过调度中心将数据的变化通知给相关的Watcher,使其能够被正确触发和更新。

通过使用发布-订阅模式,Vue2能够更加高效地捕获数据的变化,并在适当的时候进行批量、异步的视图更新。这有效地减少了不必要的渲染和提高了性能。

需要注意的是,Vue2内部实现了自己的发布-订阅系统,并非完全遵循经典的发布-订阅模式。这也是Vue2能够提供高效而灵活的响应式系统的关键之一。

四、vue2是如何收集依赖的?

Vue2通过Watcher和Dep(Dependency)对象来进行依赖的收集。

在Vue2中,当模板编译过程中遇到绑定表达式(如{{ message }})或指令(如v-model、v-bind)时,会创建对应的Watcher对象,并建立与相关数据属性之间的关联。这些Watcher对象负责监听数据变化并更新视图。

同时,每个数据属性都有一个对应的Dep对象,用于存储依赖于该数据属性的Watcher对象。这个Dep对象会管理多个Watcher对象,并在数据发生变化时通知这些Watcher进行更新。

具体的依赖收集过程如下:

  1. 在编译阶段,当遇到绑定表达式或指令时,会创建一个Watcher对象,并将其添加到当前活动的Watcher栈中。

  2. 在访问数据属性值时,数据劫持会触发getter函数。此时,Dep.target(当前活动的Watcher对象)会被设置为这个Watcher。

  3. 当getter函数执行时,会将Dep.target(即Watcher对象)添加到该数据属性的Dep依赖列表中。

  4. 重复步骤2和3,直到整个模板编译完成,所有的依赖关系都被收集起来。

  5. 当数据属性发生变化时,数据劫持会触发setter函数,并通知对应Dep对象,然后Dep对象再通知注册的所有Watcher对象进行更新。

通过这种机制,一旦数据属性发生变化,Vue2能够准确地找到依赖于该数据属性的Watcher对象,并触发它们进行视图的更新。

需要注意的是,在模板编译过程中,只有被绑定的数据属性才会被收集依赖。而不被绑定的数据属性则不会触发getter函数,也就不会将对应的Watcher添加到Dep的依赖列表中。这样可以避免不必要的依赖收集和更新。

通过Watchers、Dep对象以及Vue2的响应式机制,实现了高效的依赖收集和视图更新,保证了数据与视图之间的同步。

五、实现一个简单的vue2响应式双向绑定原理的代码及解析

// 数据劫持
function defineReactive(data, key, value) {
  let dep = new Dep();

  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      if (Dep.target) {
        dep.addWatcher(Dep.target);
      }
      return value;
    },
    set(newValue) {
      if (newValue !== value) {
        value = newValue;
        dep.notify();
      }
    },
  });
}

// Watcher 监听器
class Watcher {
  constructor(vm, key, callback) {
    this.vm = vm;
    this.key = key;
    this.callback = callback;

    // 将当前Watcher实例指定给Dep类的静态属性target
    Dep.target = this;
    // 触发一次getter,进行依赖收集
    this.vm[this.key];
    Dep.target = null; // 重置Dep类的静态属性target
  }

  // 通过回调函数更新视图
  update() {
    this.callback.call(this.vm, this.vm[this.key]);
  }
}

// 发布-订阅模式的调度中心
class Dep {
  constructor() {
    this.watchers = [];
  }

  addWatcher(watcher) {
    this.watchers.push(watcher);
  }

  notify() {
    this.watchers.forEach((watcher) => {
      watcher.update();
    });
  }
}

// Vue类
class Vue {
  constructor(options) {
    this.data = options.data;

    // 实现数据劫持
    Object.keys(this.data).forEach((key) => {
      defineReactive(this.data, key, this.data[key]);
    });

    // 创建Watcher对象,监听数据变化
    new Watcher(this, 'message', this.updateMessage);

    // 测试数据变化后是否能自动更新视图
    setTimeout(() => {
      this.data.message = 'Hello, Vue!';
    }, 2000);
  }

  // 数据变化时的回调函数
  updateMessage(newValue) {
    console.log('Updated message:', newValue);
  }
}

// 测试
const vm = new Vue({
  data: {
    message: 'Hello, World!',
  },
});

在上述代码中,首先实现了defineReactive函数用于数据劫持。然后定义了Watcher类作为监听器,它负责收集依赖和触发更新。最后,创建了一个简单的Vue类,并在构造函数中进行数据劫持和创建Watcher对象。

当数据属性发生变化时,会触发相应的setter函数并通知Dep调度中心,然后调度中心会依次通知相关的Watcher对象进行更新。

通过该示例,可以看到当data.message的值变化时,会自动触发updateMessage方法,输出更新后的消息。

这个简单的示例演示了Vue2中数据劫持、Watcher监听器和发布-订阅模式的基本原理,但并不涵盖Vue完整的功能。Vue源码中实际的实现更为复杂和全面,包含了更多的细节和优化。但这个示例可以帮助理解它们的基本工作原理。

六、结语

牵手 持续为你分享各类知识和软件 ,欢迎访问、关注、讨论 并留下你的小心心❤

标签:Dep,绑定,视图,Watcher,更新,Vue2,双向,数据
From: https://blog.51cto.com/u_15994752/6522197

相关文章

  • 探索Obsidian:效率学习与自我接纳的双向之旅
    咕~我的obsidian年度插件分享&低效率区学渣的思考~_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1NR4y1Y7Jw/本文将对笔者的Obsidian使用经验进行全面梳理和反思,深入探讨其中的魅力与困惑,揭示其背后的双链笔记法的精髓。同时,笔者也会分享一些自己认为高效且有价值的Obs......
  • 关于线性结构中的双向链表如何实现?
    前言在上一篇文章中,主要是给大家介绍了单向链表的特点及其原理,但是我们没有通过代码进行练习。今天我会继续通过一篇文章,来给大家讲解双向链表的内容,尤其是会通过代码来进行链表的操作,希望大家重点关注哦。全文大约【3500】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!......
  • WPF 实现在Combobox下拉菜单展开,未选择,直接点击button自动收起下拉菜单,并响应button绑
    在正常情况下,下拉菜单展开后,我们都会选择一个合适的选项;但是在某些时候,展开下拉菜单后,发现并不需要选择一个选项,只是需要进行后续操作,然后这时在点击其他位置的button时,你会发现:只有combobox的下拉菜单收起来了,但是button的绑定事件并未响应…… 为了实现未选择下拉菜单,点击bu......
  • 一个执行计划异常变更的案例 - 外传之查看绑定变量值的几种方法
    这篇外传之前有这么几篇文章:《一个执行计划异常变更的案例-前传》《一个执行计划异常变更的案例-外传之绑定变量窥探》上一篇文章介绍了绑定变量以及11g之前绑定变量窥探的影响,这篇文章会介绍几种查看绑定变量值的方法。上篇文章我们说了,绑定变量实际是一些占位符,可以让仅......
  • 超多绑定变量导致异常的一个案例
    最近生产上出现一个问题,某个应用单个SQL中绑定变量个数超过了65535个,导致数据库出现了异常终止的现象。通过trace,看到很多这样的信息(为了脱敏,此处引用MOS的例子),导致问题的SQL诸如这种,BEGINUPDATETESTSETC1=:1,C2=:2,C3=:3,......
  • 【数据结构】带头双向循环链表
    ......
  • 解决vue2中methods写的方法无法使用箭头函数
    1.情况:在method写递归函数,函数内使用了this.变量,报错变量为undefined,原因是function内this指向改变,因改写为箭头函数,使其this不被改变,但是methods内又无法写箭头函数 2.解决:使用var获取this,供函数内使用 ......
  • BTA41-ASEMI代理意法双向可控硅BTA41
    编辑:llBTA41-ASEMI代理意法双向可控硅BTA41型号:BT139-800E品牌:ST/意法封装:TO-3P工作温度:-40°C~150°CBTA41描述:BTA41、BTA40和BTB41有电源包可供选择,适用于一般目的交流开关。当与尺寸合适的散热器一起使用时,BTA40、BTA41和BTB41可以启用高达9千瓦的交流开关系统。请参阅ST应用说......
  • BTA41-ASEMI代理意法双向可控硅BTA41
    编辑:llBTA41-ASEMI代理意法双向可控硅BTA41型号:BT139-800E品牌:ST/意法封装:TO-3P工作温度:-40°C~150°CBTA41描述:BTA41、BTA40和BTB41有电源包可供选择,适用于一般目的交流开关。当与尺寸合适的散热器一起使用时,BTA40、BTA41和BTB41可以启用高达9千瓦的交流开关系统。请参阅S......
  • 在KVM中将USB设备绑定到虚拟机,device地址会变化的时候也可以使用该办法
    1.检查主机上的USB设备。在主机上运行`lsusb`命令,查看连接的USB设备及其总线地址,例如:$lsusbBus002Device001:ID1d6b:0003LinuxFoundation3.0roothubBus001Device003:ID046d:c52bLogitech,Inc.UnifyingReceiverBus001Device002:ID8087:0024I......