【Vue面试题十二】、说说你对双向绑定的理解?

news2024/11/17 16:27:42

文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。

面试官:双向数据绑定是什么

在这里插入图片描述

一、什么是双向绑定

我们先从单向绑定切入单向绑定非常简单,就是把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执行更新方法  
    },  
  });  
}  

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

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

相关文章

VMware Workstation Player 17 下载安装教程

虚拟机系列文章 VMware Workstation Player 17 免费下载安装教程 VMware Workstation 17 Pro 免费下载安装教程 windows server 2012安装教程 Ubuntu22.04.3安装教程 FTP服务器搭建 VMware Workstation Player 17 下载安装教程 虚拟机系列文章前言一、 VMware Workstation Pla…

是炒作还是大势所趋?电商企业7 种日渐盛行的 API 管理应用场景

17 世纪兴起的纸质货币改变了全球经济的面貌,开创了国际货币监管的新时代。货币能带来如此重大的颠覆,主要原因在于它有着标准化“交换媒介”的能力。在技术和数字化世界中,API 产生了类似的效果,它创建了标准化、可重复使用、安全…

CSS 中::after的妙用(实现在margin中显示内容)

效果图如下: 背景: 如上图,之前只是当纯的写一个参考货架平面图,用作物料系统的在库状态可视化,当完成页面body分成10等份时,货架之间需要有通道,为了实现实际的样式,我给每个等份都…

FPGA实现电机霍尔编码器模块

一. 简介 想要知道直流电机的转速,就需要用到编码器,常用的编码器有霍尔和光电两种,但是光电编码器比较贵(性能好于霍尔),所以平常的时候使用最多的是霍尔编码器了。 霍尔编码器一般有AB两相信号输出,默认的时候为低…

AB试验(五)实验过程中的一些答疑解惑

AB试验(五)实验过程中的一些答疑解惑 实验结果不显著怎么办 出现实验结果不显著的原因 A/B测试的变化确实没有效果,两组指标在事实上是相同的A/B测试的变化有效果,但由于变化的程度很小,测试灵敏度power不足&#x…

Linux 安全 - LSM机制

文章目录 前言一、LSM起源二、LSM简介2.1 MAC2.2 LSM特征 三、Major and Minor LSMs3.1 Major LSMs3.2 Minor LSMs3.3 BPF LSM 四、LSM 框架五、LSM Capabilities Module六、LSM hooks 说明参考资料 前言 在这两篇文章中介绍了 Linux 安全机制 Credentials : Linu…

关于GD32引脚PA13、PA15、PB3、PB4配置为普通引脚的问题

关于GD32引脚PA13、PA15、PB3、PB4配置为普通引脚的问题 在实际开发中,经常会遇到引脚资源受限需要将一些具有特定功能的引脚配置为普通引脚或其他引脚功能使用的情况。 博主之前遇到过类似的情况,都正常解决了。但偶尔也会出现在配置引脚时少了一些配…

Go语言基础学习教程(一)导学部分

Go语言基础 将在工作中学习的东西整理出来作为一个简单的Go语言基础教程 Go语言 Go语言(Golang)是Google公司2009年推出的一门"高级编程言语", 目的是为了解决: "现有主流编程语言"明显落后于硬件发展速度的问题不能合理利用多核CPU的优势提升软件系统性…

ref与DOM-findDomNode-unmountComponentAtNode知识点及应用例子

​​​​​​http​​​http://t.csdnimg.cn/og3BI 知识点讲解↑ 需求: (下载/导出 用post请求时:) 实例: react部分代码 1、点击下载按钮,需要传给后端数据,到数据扁平,不是那么复杂,只需url地址即可完成下载,后端…

靶场通关记录

目录 一、信息收集-端口扫描 目标开放端口收集 目标端口对应服务探测 信息收集-端口测试 二、 22-SSH弱口令爆破(挂着干别的) 80-HTTP端口的信息收集 三、信息收集-目录访问 漏洞利用-getwebshell 总结 getwebshell → 源码注释发现用户名 → robots.txt发现base64密码 …

mybatisplus接口解析

一、前言 java开发应用程序与数据库交互使用比较多的就是mybatisplus接口。通过mybatisplus接口,我们可以通过程序更好得到对数据库表进行增删查改。 二、mybatisplus Mapper接口源码 /*** Mapper 继承该接口后,无需编写 mapper.xml 文件,即…

1576. 替换所有的问号

1576. 替换所有的问号 C代码&#xff1a;自己写的 char * modifyString(char * s){int n strlen(s);for (int i 0; i < n; i){if (s[i] ?) {if (i ! 0 && i ! n-1) {for (int j 0; j < 26; j) {if (a j ! s[i-1] && a j ! s[i1]) {s[i] a j;br…

SeleniumIDE 自动化用例录制、测试用例结构分析

1、SeleniumIDE用例录制 SeleniumIDE用例录制使用场景 刚开始入门UI自动化测试团队代码基础较差技术成长之后学习价值不高 SeleniumIDE的下载以及安装 官网&#xff1a;https://www.selenium.dev/Chrome插件&#xff1a;https://chrome.google.com/webstore/detail/seleniu…

对音频切分成小音频(机器学习用)

我是把so-vits中小工具&#xff0c;分析源码然后提取出来了。以后可以写在自己的程序里。 -------流程&#xff08;这是我做的流程&#xff0c;你可以不用看&#xff09; 从开源代码中快速获取自己需要的东西 如果有界面f12看他里面的接口&#xff0c;然后在源码中全局搜索&…

英国/法国/意大利/德国/西班牙,电动交通设备合规政策更新!

英国/法国/意大利/德国/西班牙&#xff0c;电动交通设备合规政策更新&#xff01; 产品安全 合规政策更新&#xff01;或加昵称咨询 NOTICE 尊敬的卖家&#xff1a; 您好&#xff01; 我们此次联系您是因为您正在销售需要审批流程的商品。为此&#xff0c;亚马逊正在实施审…

九大装修收纳空间的设计,收藏备用!福州中宅装饰,福州装修

如果房子面积不大&#xff0c;收纳设计就显得非常重要。其实装修房子中很多地方都可以做收纳&#xff0c;九大空间每一处都可以放下你的东西&#xff0c;让你摆脱收纳烦恼。 收纳空间少的话&#xff0c;装修完后住久了怕会乱成一窝&#xff0c;因此装修的时候&#xff0c;收纳…

google hack常用语法介绍

Google hacker (Google黑客)是利用GOOGLE提供的搜索功能查找黑客们想找到的信息.一般是查找网站后台,网管的个人信息,也可以用来查找某人在网络上的活动. Google hacker 一般是做为黑客在入侵时的一个手段.在入侵过程中有时需要查找后台的登陆口就需要用到GOOGLE HACKER.有…

在asp.net中,实现类似安卓界面toast的方法(附更多弹窗样式)

最近在以前的asp.net网页中&#xff0c;每次点击确定都弹窗&#xff0c;然后还要弹窗点击确认&#xff0c;太麻烦了&#xff0c;这次想升级一下&#xff0c;实现类似安卓toast的弹窗提示方式。于是百度了一下&#xff0c;目前看到有两种&#xff0c;sweetalert和toastr。 这里…

Python接口自动化测试实战详解,你想要的全都有

前言 接口自动化测试是当前软件开发中最重要的环节之一&#xff0c;可以提高代码质量、加速开发周期、减少手工测试成本等优点。Python语言在接口自动化测试方面应用广泛&#xff0c;因为它具有简单易学、开发效率高、库丰富等特点。 一、接口自动化测试概述 接口自动化测试…

六个交易日市值蒸发20亿港元,第四范式难逃AI大模型“魔咒”

AI独角兽第四范式终于敲钟了。 北京第四范式智能技术股份有限公司(06682.HK&#xff0c;下称“第四范式”)于9月28日正式挂牌港交所&#xff0c;发行价为55.60港元/股&#xff0c;IPO首日报收58.50港元/股。 上市后6个交易日&#xff0c;截至10月6日港股收盘&#xff0c;第四…