Vue.js 中的数据双向绑定是如何实现的?

news2024/9/24 15:26:20

Vue.js 中的数据双向绑定是如何实现的?

Vue.js 是一款流行的前端框架,它的核心功能之一是数据双向绑定。本文将介绍 Vue.js 中数据双向绑定的实现原理,并附上相关代码实例。

在这里插入图片描述

什么是数据双向绑定?

在传统的前端开发中,当用户在界面上修改数据时,需要手动更新数据模型,反之亦然。这种方式不仅繁琐,而且容易出错。数据双向绑定可以解决这个问题。它是一种自动同步数据模型和界面的机制,即当数据模型发生变化时,界面会自动更新,反之亦然。

Vue.js 中的数据双向绑定实现原理

Vue.js 中的数据双向绑定是通过数据劫持和发布-订阅模式来实现的。

数据劫持

Vue.js 中的数据劫持是通过 Object.defineProperty 方法来实现的。它可以在一个对象上定义一个新属性,或者修改一个已有属性,并指定该属性的一些特性,例如值、可枚举性、可写性和可配置性等。

在 Vue.js 中,当一个组件创建时,它会遍历所有的属性,对于其中的对象类型,会递归地对其做数据劫持。例如:

function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get: function() {
      console.log(`获取 ${key}: ${val}`);
      return val;
    },
    set: function(newVal) {
      console.log(`设置 ${key}: ${newVal}`);
      val = newVal;
    }
  })
}

const data = { name: 'Tom', age: 18 };
defineReactive(data, 'name', 'Tom');
defineReactive(data, 'age', 18);

data.name; // 获取 name: Tom
data.age; // 获取 age: 18

data.name = 'Jerry'; // 设置 name: Jerry
data.age = 20; // 设置 age: 20

在上面的代码中,defineReactive 函数接收三个参数,分别是要做数据劫持的对象、属性名和属性值。在 defineReactive 函数中,使用 Object.defineProperty 方法来定义对象的属性,并为其设置 getset 方法。当获取属性值时,会触发 get 方法,当设置属性值时,会触发 set 方法。

通过这种方式,Vue.js 可以监听到数据模型的变化,并及时更新界面。

发布-订阅模式

Vue.js 中的发布-订阅模式是通过一个事件中心来实现的。事件中心是一个全局的事件管理器,用于管理所有的事件监听和触发。在 Vue.js 中,事件中心被封装在 Vue 对象的原型上,即 Vue.prototype.$emitVue.prototype.$on 方法。

$emit 方法用于触发一个事件,并将数据传递给所有订阅该事件的回调函数。例如:

const eventBus = new Vue();

eventBus.$on('hello', function(data) {
  console.log(`收到 hello 事件,数据为 ${data}`);
});

eventBus.$emit('hello', 'world'); // 收到 hello 事件,数据为 world

在上面的代码中,我们创建了一个事件中心 eventBus,并使用 $on 方法订阅了一个名为 hello 的事件,当该事件被触发时,会执行回调函数并打印出数据。然后,我们使用 $emit 方法触发了一个 hello 事件,并将数据传递给回调函数。

通过发布-订阅模式,Vue.js 可以监听到数据模型的变化,并及时更新界面。

结合数据劫持和发布-订阅模式实现数据双向绑定

在 Vue.js 中,数据双向绑定是通过结合数据劫持和发布-订阅模式来实现的。具体实现步骤如下:

  1. 创建一个 Observer 对象,用于对数据模型进行数据劫持。
  2. 创建一个 Dep 对象,用于管理所有的订阅者。
  3. 创建一个 Watcher 对象,用于订阅数据模型的变化。
  4. Watcher 对象添加到 Dep 对象中。
  5. 当数据模型发生变化时,Observer 对象会通过 Dep 对象通知所有的订阅者,订阅者会自动更新界面。

下面是一个简单的实现示例:

// Observer 对象,用于对数据模型进行数据劫持
function Observer(data) {
  this.data = data;
  this.walk(data);
}

Observer.prototype = {
  walk: function(data) {
    var self = this;
    Object.keys(data).forEach(function(key) {
      self.defineReactive(data, key, data[key]);
    });
  },
  defineReactive: function(data, key, val) {
    var dep = new Dep();

    Object.defineProperty(data, key, {
      enumerable: true,
      configurable: false,
      get: function() {
        if (Dep.target) {
          dep.addSub(Dep.target);
        }
        return val;
      },
      set: function(newVal) {
        if (newVal === val) {
          return;
        }
        val = newVal;
        dep.notify();
      }
    });
  }
};

// Dep 对象,用于管理所有的订阅者
function Dep() {
  this.subs = [];
}

Dep.prototype = {
  addSub: function(sub) {
    this.subs.push(sub);
  },
  notify: function() {
    this.subs.forEach(function(sub) {
      sub.update();
    });
  }
};

Dep.target = null;

// Watcher 对象,用于订阅数据模型的变化
function Watcher(vm, exp, cb) {
  this.vm = vm;
  this.exp = exp;
  this.cb = cb;

  this.value = this.get();
}

Watcher.prototype = {
  update: function() {
    var value = this.vm.$data[this.exp];
    var oldValue = this.value;
    if (value !== oldValue) {
      this.value = value;
      this.cb.call(this.vm, value, oldValue);
    }
  },
  get: function() {
    Dep.target = this;
    var value = this.vm.$data[this.exp];
    Dep.target = null;
    return value;
  }
};

在上面的代码中,我们定义了 ObserverDepWatcher 三个对象。Observer 对象用于对数据模型进行数据劫持,Dep 对象用于管理所有的订阅者,Watcher 对象用于订阅数据模型的变化。

Observer 对象中,我们使用 defineReactive 方法对数据模型进行数据劫持,并为其设置 getset 方法。在 get 方法中,我们将订阅者添加到 Dep 对象中,以便在数据发生变化时可以通知到所有的订阅者。在 set 方法中,我们将新值赋给数据模型,并通过 Dep 对象通知所有的订阅者。

Dep 对象中,我们使用 addSub 方法将订阅者添加到订阅者列表中,使用 notify 方法通知所有的订阅者。

Watcher 对象中,我们使用 update 方法更新界面,并将新值和旧值传递给回调函数。在 get 方法中,我们将当前订阅者添加到 Dep 对象中,并获取数据模型的值。

通过以上实现,Vue.js 可以自动同步数据模型和界面,并实现数据双向绑定。下面是一个简单的使用示例:

// 创建 Vue 实例
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello, Vue.js!'
  }
});

// 订阅数据模型的变化
new Watcher(vm, 'message', function(value, oldValue) {
  console.log(`数据模型从 ${oldValue} 变为 ${value}`);
});

// 修改数据模型
vm.$data.message = 'Hello, World!'; // 数据模型从 Hello, Vue.js! 变为 Hello, World!

在上面的代码中,我们创建了一个 Vue 实例,并定义了一个名为 message 的数据模型。然后,我们创建了一个订阅者,用于监听数据模型的变化,并在控制台打印出新值和旧值。最后,我们修改了 message 数据模型的值,触发了数据劫持和发布-订阅模式,订阅者自动更新界面。

总结

数据双向绑定是 Vue.js 的核心功能之一,它可以自动同步数据模型和界面,提高开发效率和代码质量。在 Vue.js 中,数据双向绑定是通过结合数据劫持和发布-订阅模式来实现的。在数据劫持中,Vue.js 使用 Object.defineProperty 方法对数据模型进行监听;在发布-订阅模式中,Vue.js 使用一个事件中心来管理所有的事件监听和触发。通过这两种技术的结合,Vue.js 实现了数据双向绑定,为前端开发带来了很大的便利。

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

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

相关文章

Matlab论文插图绘制模板第98期—大小不同多子图(Subplot)

上一篇文章分享了Matlab多子图的绘制模板: 但假如子图的大小不是相同的,该怎么操作呢? 本期就来分享一下大小不同多子图的绘制模板。 先来看一下成品效果: 特别提示:本期内容『数据代码』已上传资源群中,…

java SSM 房屋管理系统统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 房屋管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和 数据库,系统主要采用B…

Linux - 第22节 - 网络基础(其他重要协议或技术)

1.DNS协议 • DNS(Domain Name System,域名系统)协议,是一个用来将域名转化为IP地址的应用层协议。 • DNS协议属于应用层协议,由UDP实现其域名解析功能。 1.1.DNS背景 TCP/IP中通过IP地址和端口号的方式,来…

地震勘探基础(七)之地震静校正

地震静校正 首先,为什么要进行地震静校正处理呢?主要的原因是地震勘探中激发和接收的观测面不完全是水平的。尤其是在山区、沙漠和黄土原地区。而且近地表还存在风化层或低、降速带低、降速带的厚度和速度会发生变化,这就导致反射波的传播时…

HTML5 FormData对象

利用FormData对象,你可以使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest发送这个"表单". 创建一个FormData对象 你可以先创建一个空的FormData对象,然后使用append()方法向该对象里添加字段,如下: var oMyForm new FormData();oMyForm.append(&…

Web服务器的工作原理

Web服务器的工作原理 什么是web服务器、应用服务器和web容器?什么是Servlet?他们有什么作用?什么是ServletContext?它由谁创建?ServletRequest和ServletResponse从哪里进入生命周期?如何管理Session&#x…

界面开发框架Qt新手入门教程:Dir视图使用实例

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 点击获取Qt Widget组…

容器(第一篇)docker安装、基础操作命令

docker是什么? docker是一个go语言开发的应用容器引擎。 docker的作用? ①运行容器里的应用; ②docker是用来管理容器和镜像的一种工具。 容器 与 虚拟机 的区别? 容器 虚拟机所有容器共享宿主机…

AIGC大模型之——以文生图介绍

一、什么是以文生图? 以文生图是AIGC ( AI Generated Content )框架中的一个关键技术,通过文字描述,将文字转化为图像并展示出来。以文生图具有白动化程度高、精度高、可扩展性强、可定制化等优势,具有广泛的应用前景&…

PyTorch 提示和技巧:从张量到神经网络

张量和梯度 我们将深入探讨使用 PyTorch 构建自己的神经网络必须了解的 2 个基本概念:张量和梯度。 张量 张量是 PyTorch 中的中央数据单元。它们是类似于数组的数据结构,在功能和属性方面与 Numpy 数组非常相似。它们之间最重要的区别是 PyTorch 张量…

Hadoop中HDFS概述

Hadoop概述之HDFS HDFS架构概述优缺点HDFS架构HDFS文件块大小HDFS的shell命令HDFS读写流程写数据流程 HDFS读数据流程NameNode 和 SecondaryNameNode工作机制DataNode工作机制DataNode数据完整性如何保证 端口名称Hadoop2.xHadoop3.xNameNode内部通信端口8020/9000NameNode HTT…

【STM32单片机】基于语音识别的智能分类垃圾桶,ld3320语音识别模块如何使用,mp3播放模块如何使用

文章目录 需求语音识别模块MY1690 播放模块舵机源码 需求 对于“可回收物”“有害垃圾”“厨余垃圾”“其它垃圾”,不能分清扔到哪个垃圾桶怎么办? 基于语音识别的智能分类垃圾桶,识别到关键词就打开对应的垃圾桶,完全没有分不清…

echarts中彻底清除所有实例和相关数据

单个实例 dispose销毁实例&#xff0c;销毁后实例无法再被使用。 myChart.dispose();实例比较多的时候 获取Dom元素 let doms document.getElementsByClassName(my-chart)销毁所有实例 if(doms && doms.length) {for (let i 0; i < doms.length; i) {let chartIn…

Jumpserver 2.28.8使用分享

目录 一、Jumpserver 介绍 1、跳板机和堡垒机理解 1.1、跳板机 1.2、堡垒机 2、jumpserver简介 二、Jumpserver 安装部署 2.1、部署规划 2.2 、安装要求 JumpServer 环境要求: 2.3、安装方法介绍 官方提供了多种安装方法 三、Jumpserver平台使用 3.1、Admin登录 3.…

bug(Tomcat):StandardContext.startInternal 由于之前的错误,Context[/day01]启动失败

引出 项目启动失败&#xff0c;一个困扰了一上午的bug 报错信息 org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到 org.apache.catalina.core.StandardContext.startInternal 由于之前的错误…

骨传导是哪个意思,推荐几款性能优的骨传导耳机

​骨传导耳机是通过头部骨迷路传递声音&#xff0c;而不是直接通过耳膜的振动来传递声音。与传统的入耳式耳机相比&#xff0c;骨传导耳机不会堵耳朵&#xff0c;在跑步、骑车等运动时可以更好的接收外界环境音&#xff0c;保护听力&#xff0c;提升安全性。此外&#xff0c;骨…

图解LeetCode——114. 二叉树展开为链表

一、题目 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同。 二…

公式+ChatGPT:为你的标题创作注入新鲜活力

大家是不是经常遇到文章已经写好了&#xff0c;但是标题却还空着&#xff0c;不是不会写&#xff0c;就是写得平淡无奇&#x1f602;。自己都觉得无趣的标题又怎么能吸引有趣的灵魂呢&#xff1f;何不让chatGPT来试试呢&#xff1f; 首先&#xff0c;我们要明白一个基础理念&am…

微软 AD 已成过去式,这个身份领域国产化替代方案你了解吗?

随着全球互联网和数字化浪潮的不断发展&#xff0c;信息安全已成为不可忽视的问题&#xff0c;并随着日益复杂的国内外市场格局&#xff0c;其重要性更加凸显。我国政府也相继印发和实施了《数字中国建设整体布局规划》、《全国一体化大数据体系建设指南》等一系列政策&#xf…

【JavaEE】Servlet的API详解

Servlet的API详解O(∩_∩)O~&#xff1a; 文章目录 JavaEE & Servlet的API详解1. HttpServlet抽象类1.1 init方法1.2 destroy方法1.3 service方法 2. HttpRequest接口2.1 在浏览器上显示请求首行2.2 在浏览器上显示请求header2.3 getParameter方法 - 最常用的API之一2.4 js…