揭秘Vue框架下的MVVM架构:数据双向绑定背后的技术细节

本文主要是介绍揭秘Vue框架下的MVVM架构:数据双向绑定背后的技术细节,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  

 🎬 江城开朗的豌豆:个人主页

 🔥 个人专栏 :《 VUE 》 《 javaScript 》

 📝 个人网站 :《 江城开朗的豌豆🫛 》 

⛺️ 生活的理想,就是为了理想的生活 !

目录

 ⭐  专栏简介

 

一、什么是双向绑定

二、双向绑定的原理是什么

理解ViewModel

三、实现双向绑定

实现

编译Compile

依赖收集

⭐  写在最后


 

 ⭐  专栏简介

        欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。

        同时,我们也会关注最新的前端趋势和发展动态。随着Web技术的不断演进,前端开发也在不断推陈出新。我们会及时介绍最新的前端框架、工具和技术,使你能够站在前沿,与时俱进。通过掌握最新的前端技术,你将能够在竞争激烈的Web开发领域中有更大的竞争力。

 

一、什么是双向绑定

我们先从单向绑定切入单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新双向绑定就很容易联想到了,在单向绑定的基础上,用户更新了ViewModel的数据也自动被更新了,这种情况就是双向绑定举个例子

当用户填写表单时,View的状态就被更新了,如果此时可以自动更新Model的状态,那就相当于我们把ModelView做了双向绑定关系图如下

二、双向绑定的原理是什么

我们都知道 Vue 是数据双向绑定的框架,双向绑定由三个重要部分构成

  • 数据层(Model):应用的数据及业务逻辑
  • 视图层(View):应用的展示效果,各类UI组件
  • 业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来

而上面的这个分层的架构方案,可以用一个专业术语进行称呼:MVVM这里的控制层的核心功能便是 “数据双向绑定” 。自然,我们只需弄懂它是什么,便可以进一步了解数据绑定的原理

理解ViewModel

它的主要职责就是:

  • 数据变化后更新视图
  • 视图变化后更新数据

当然,它还有两个主要部分组成

  • 监听器(Observer):对所有数据的属性进行监听
  • 解析器(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数

三、实现双向绑定

我们还是以Vue为例,先来看看Vue中的双向绑定流程是什么的

  1. new Vue()首先执行初始化,对data执行响应化处理,这个过程发生Observe
  2. 同时对模板执行编译,找到其中动态绑定的数据,从data中获取并初始化视图,这个过程发生在Compile
  3. 同时定义⼀个更新函数和Watcher,将来对应数据变化时Watcher会调用更新函数
  4. 由于data的某个key在⼀个视图中可能出现多次,所以每个key都需要⼀个管家Dep来管理多个Watcher
  5. 将来data中数据⼀旦发生变化,会首先找到对应的Dep,通知所有Watcher执行更新函数

流程图如下:

实现

先来一个构造函数:执行初始化,对data执行响应化处理

class Vue {  constructor(options) {  this.$options = options;  this.$data = options.data;  // 对data选项做响应式处理  observe(this.$data);  // 代理data到vm上  proxy(this);  // 执行编译  new Compile(options.el, this);  }  
}  

data选项执行响应化具体操作

function observe(obj) {  if (typeof obj !== "object" || obj == null) {  return;  }  new Observer(obj);  
}  class Observer {  constructor(value) {  this.value = value;  this.walk(value);  }  walk(obj) {  Object.keys(obj).forEach((key) => {  defineReactive(obj, key, obj[key]);  });  }  
}  
编译Compile

对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数

class Compile {  constructor(el, vm) {  this.$vm = vm;  this.$el = document.querySelector(el);  // 获取dom  if (this.$el) {  this.compile(this.$el);  }  }  compile(el) {  const childNodes = el.childNodes;   Array.from(childNodes).forEach((node) => { // 遍历子元素  if (this.isElement(node)) {   // 判断是否为节点  console.log("编译元素" + node.nodeName);  } else if (this.isInterpolation(node)) {  console.log("编译插值⽂本" + node.textContent);  // 判断是否为插值文本 {{}}  }  if (node.childNodes && node.childNodes.length > 0) {  // 判断是否有子元素  this.compile(node);  // 对子元素进行递归遍历  }  });  }  isElement(node) {  return node.nodeType == 1;  }  isInterpolation(node) {  return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);  }  
}  
依赖收集

视图中会用到data中某key,这称为依赖。同⼀个key可能出现多次,每次都需要收集出来用⼀个Watcher来维护它们,此过程称为依赖收集多个Watcher需要⼀个Dep来管理,需要更新时由Dep统⼀通知

实现思路

  1. defineReactive时为每⼀个key创建⼀个Dep实例
  2. 初始化视图时读取某个key,例如name1,创建⼀个watcher1
  3. 由于触发name1getter方法,便将watcher1添加到name1对应的Dep中
  4. name1更新,setter触发时,便可通过对应Dep通知其管理所有Watcher更新
// 负责更新视图  
class Watcher {  constructor(vm, key, updater) {  this.vm = vm  this.key = key  this.updaterFn = updater  // 创建实例时,把当前实例指定到Dep.target静态属性上  Dep.target = this  // 读一下key,触发get  vm[key]  // 置空  Dep.target = null  }  // 未来执行dom更新函数,由dep调用的  update() {  this.updaterFn.call(this.vm, this.vm[this.key])  }  
}  

声明Dep

class Dep {  constructor() {  this.deps = [];  // 依赖管理  }  addDep(dep) {  this.deps.push(dep);  }  notify() {   this.deps.forEach((dep) => dep.update());  }  
}  

创建watcher时触发getter

class Watcher {  constructor(vm, key, updateFn) {  Dep.target = this;  this.vm[this.key];  Dep.target = null;  }  
}  

依赖收集,创建Dep实例

function defineReactive(obj, key, val) {  this.observe(val);  const dep = new Dep();  Object.defineProperty(obj, key, {  get() {  Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例  return val;  },  set(newVal) {  if (newVal === val) return;  dep.notify(); // 通知dep执行更新方法  },  });  
}  

⭐  写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

这篇关于揭秘Vue框架下的MVVM架构:数据双向绑定背后的技术细节的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/363893

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo