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

news2024/11/24 14:44:06

输入图片描述

一、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源码中实际的实现更为复杂和全面,包含了更多的细节和优化。但这个示例可以帮助理解它们的基本工作原理。

六、结语

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/668377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【单元测试】Junit 4教程(一)--白盒测试方法

目录 1.0 流程图标识 1.1 语句覆盖法(C0标准) 1.2 判定/分支覆盖法(C1标准) 1.3 条件覆盖法(C2标准) 1.4 判定条件覆盖法(C1C2标准) 1.5 条件组合覆盖法(C3标准&am…

【ESP8266】基础AT指令和常用WIF指令

【ESP8266 (12F)】硬件参数 以及 固件烧录 文章目录 一、常用AT命令1.1 基础1.2 WiFi相关1.21 ATCWMODE:查询/设置 Wi-Fi 模式 (Station/SoftAP/StationSoftAP)1.22 ATCWJAP:连接 AP1.23 ATCWLAP:扫描当前可用的 AP1.2…

容器JVM内存配置最佳实践

背景信息 当您的业务是使用Java开发,且设置的JVM堆空间过小时,程序会出现系统内存不足OOM(Out of Memory)的问题。事件中心的OOM事件是指系统内存不足时,触发了Linux的内存回收(OOM Killer)机制…

7D性能工程初级班第一期开班了!

Slogan:领略性能艺术的壮阔、感受性能测试的博大精深 课程大纲见:【7D-RESAR 性能工程初级班大纲】 报名流程 讲师介绍 高楼老师: 性能领域公认的具有匠心的技术专家。架构级性能解决方案资深专家。性能测试调优分析18年经验,…

Java中的实体类为什么要 implements Serializable?

1. 序列化和反序列化 首先来解释一下什么是序列化和反序列化: 序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。 在 Java 和其他语言进行通信的时候,需要将对象…

安卓手机ROOT和刷机基本操作——以红米Note7刷安卓原生系统并Root为例

文章目录 前言一.简介1. 安卓权限2. 安卓分区Boot分区System分区Data分区Cache分区Recovery分区 3. Fastboot 二.前置准备1. Android SDK 工具2. 解BL锁 三. ROOT1.Fastboot线刷(推荐)获取系统boot镜像修补boot.img刷入boot 2. Recovery卡刷(可以尝试) 四. 红米Note7刷安卓原生…

Burpsuite介绍及2022.8.2版本超详细安装教程(图文版)

Burpsuite介绍及2022.8.2版本超详细安装教程(图文版) 文章目录 Burpsuite介绍及2022.8.2版本超详细安装教程(图文版)Burpsuite是什么?Burpsuite环境配置及安装JDK选择及配置Burpsuite下载安装 Burpsuite快捷启动方式选…

卷积神经网络中池化层的详细介绍

卷积神经网络自2012年,到2023年经历了翻天覆地的变化。最早的卷积神经网络由卷积层、池化层和全连接层所构成。其中卷积层用于提取图像的特征,池化层削减特征数量,全连接层用于对特征进行非线性组合并预测类别。然而在transformer横行的年代&…

【瑞吉外卖】适合速成SpringBoot和MyBatis的作业项目

文章目录 零、MyBatisPlus一、管理端登录1.0 统一的返回结果Result类1.1 admin/login1.2 admin/logout1.3 Filter1.4 自定义消息转换器 二、员工管理2.1 新增员工-字段填充2.2 全局异常捕获2.3 员工信息分页查询 三、分类管理3.1 分类的删除 四、菜品管理4.1 文件的上传与下载1…

CNAPPs投资热度持续攀升 腾讯云被Gartner评为全球案例厂商

近日,Gartner发布《新兴技术:在三重挤压中蓬勃发展—对云安全风险投资的关键洞察》(Emerging Tech: Thriving Amid the Triple Squeeze— Critical Insights on VC Funding for Cloud Security)(以下简称《报告》&…

Flink 学习五 Flink 时间语义

Flink 学习五 Flink 时间语义 1.时间语义 在流式计算中.时间是一个影响计算结果非常重要的因素! (窗口函数,定时器等) Flink 可以根据不同的时间概念处理数据。 处理时间: process time System.currentTimeMillis()是指执行相应操作的机器系统时间(也称为纪元时间…

优化|如何减小噪声和误差对梯度下降法的影响

编者按: ​ 许多精确算法在理论上能保证我们的目标函数值一直下降。在随机梯度下降以及无导数优化等情况下,目标移动方向受到噪声干扰,与实际下降方向往往会存在偏差。本文将分析噪声和下降偏差对于梯度下降法等算法的影响,并且介…

SpringMVC08:拦截器+文件下载

目录 一、概述 二、自定义拦截器 1、新建一个Moudule,SpringMVC-07-Interceptor,添加web支持; 2、配置web.xml和springmvc-servlet.xml文件 3、编写一个拦截器 4、在springmvc的配置文件中配置拦截器 5、编写一个Controller&#xff0…

【数据库】Mysq备份与恢复

文章目录 一、数据库备份的分类1. 数据备份的重要性2. 数据库备份的分类3. 常见的备份方法 二、Mysql 完全备份与恢复1. Mysql 完全备份2. 数据库完全备份分类2.1 物理冷备份及恢复2.2 mysqldump 备份数据库完全备份一个或多个完整的库(包括其中所有的表&#xff09…

基于YOLOv5实现安全帽检测识别

目录 1、作者介绍2、YOLOv5网络模型2.1 算法简介2.2 数据集介绍2.2.1 VOC数据集准备2.2.2 YOLOv5算法检测流程 3、代码实现3.1 数据集划分部分代码3.2 训练阶段3.3 测试阶段3.4 检测结果 4、问题与分析参考链接 1、作者介绍 陈梦丹,女,西安工程大学电子…

【6.20】sleep()和wait()的区别

sleep()和wait()的区别 1、wait()方法 1.1使用场景 当某个线程获取到锁后,却还是不满足执行的条件,就可以调用对象锁的wait方法,进入等待状态。 直到外在条件满足了,就可以由其它线程调用notify或者notifyAll方法,…

在软件研发排期中要求“倒推时间”,项目结束后悲剧了……

有没有遇到某个项目任务的研发周期已被各路boss定下,研发团队都觉得时间不合理,反馈给上级无果,而要求“倒推时间”进行任务排期的情况? 什么是“倒推时间”? 目标倒推法,从剩下的时间反推算出每天该做的事…

【Java】死锁问题及ThreadLocal

什么是死锁分析过程发生死锁的原因避免死锁ThreadLocal 什么是死锁 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。这是一个最严重的BUG之一。 分析过程 1.一个线程一把锁 一个线…

深入理解TDD(测试驱动开发):提升代码质量的利器

在日常的软件开发工作中,我们常常会遇到这样的问题:如何在繁忙的项目进度中,保证我们的代码质量?如何在不断的迭代更新中,避免引入新的错误?对此,有一种有效的开发方式能帮助我们解决这些问题&a…

14.处理大数据集

14.1 随机梯度下降 假设你正在使用梯度下降来训练一个线性回归模型 当m个样本的m很大时,求和计算量太大了。这种梯度下降算法有另外一个名字叫做批量梯度下降(batch gradient desent)。这种算法每次迭代需要使用全量训练集,直到算…