vue2响应式原理+模拟实现v-model

news2024/10/5 20:22:20

效果

简述原理

配置对象传入vue实例

模板解析,遍历出所有文本节点,利用正则替换插值表达式为真实数据

data数据代理给vue实例,以后通过this.xxx访问

给每个dom节点增加观察者实例,由观察者群组管理,内部每一个键值含有多个对不同dom的观察者

data数据劫持,给data的每个属性增加get和set函数,当值改变时触发观察者的update方法,更新所有与当前属性值相关的dom元素

劫持数据,说的挺好听的,就是加工数据嘛,多了set变化触发了模板重新渲染,该渲染方式使用观察者模式,获取观察者收集的各个dom的所有属性 div,观察的属性,div的属性textContent,同时根据最新值渲染模板

div.textContent=vm[key]

html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- <script src="./vue.js"></script> -->
</head>

<body>
  <div id="app">
    {{ name }} {{age}}
    <h1>{{age}}</h1>
    <button @click="cli">按钮</button>
    <input type="text" v-model="name">
  </div>
</body>
<script src="./vue.js">
</script>
<script>

  new Vue({
    el: '#app',
    data: {
      name: 'Zwwwww',
      age: 18,
    },
    methods: {
      cli() {
        console.log(this);
        console.log(this.age);
      }
    },
  })

</script>

</html>

js代码

class Vue {
  constructor(options) {
    // 获取配置对象的节点,存放在vm$el身上
    this.$el = document.querySelector(options.el)
    // console.log(this.$el)
    // 将配置对象的data对象代理到$data
    this.$data = options.data
    // 获取配置对象的method值,
    // vue实例监听,当触发了方法执行对应函数
    this.$methods = options.methods
    // 代理数据,后续通过this调用data对象的值
    this.$allWatcher = {}
    this.proxyData()
    // 劫持数据,为其增加观察者监视数据变化引起视图渲染
    this.observe()
    // 收集所有观察者,用对象的属性存放
    this.compile(this.$el)
  }

  // 数据代理到vue实例身上,后续this调用方法和data值
  proxyData() {
    // 遍历$data身上所有key
    for (let key in this.$data) {
      // 数据代理给vue实例,this
      Object.defineProperty(this, key, {
        // 使用get和set后续触发获取值和设置值做额外操作
        get() {
          // 返回当前data对应的key属性值
          return this.$data[key]
        },
        set(value) {
          // 设置新值给当前属性
          this.$data[key] = value
        },
      })
    }
  }
  // js数据替换{{name}},模板解析
  compile(node) {
    // 遍历根节点下的所有节点
    node.childNodes.forEach((item, index) => {
      //递归元素节点,
      //如果还没到文本节点,也就是说元素节点内还有元素节点
      //则继续递归,直到元素节点没有子节点

      //第二种可能,如果为元素元素节点,判断是否有@click属性,并获取值
      //该值为绑定的methods方法
      if (item.nodeType === 1) {
        if (item.childNodes.length > 0) {
          this.compile(item)
        }
        if (item.hasAttribute('@click')) {
          let domKey = item.getAttribute('@click')
          // console.log('我是dom标签的key', domKey)
          // 设置监听器,如果被点击了,触发配置对象中的method函数
          item.addEventListener('click', () => {
            // 通过模板获取的属性值方法命,调用函数
            // 由于$methods只是引用地址,this指向还是原来的methods
            // 我们这里使用call来绑定他的上下文this,也就是绑定他的调用者
            // 在html部分我们就可以使用this.$data.age来获取vue实例上的数据
            // 如果我们想直接this.age 就需要将data代理到vue实例身上
            this.$methods[domKey.trim()].call(this)
          })
        }
        if (item.hasAttribute('v-model')) {
          let vmodelKey = item.getAttribute('v-model').trim()
          // console.log('我是v-model的key', vmodelKey)
          // 设置监听器,如果被点击了,触发配置对象中的method函数
          // 先单向给input框设置值
          item.value = this.$data[vmodelKey]
          item.addEventListener('input', () => {
            console.log('用户正在输入')
            // 每次输入时将输入框的值重新赋给data对象属性值,完成双向绑定
            this.$data[vmodelKey] = item.value
            console.log(this.$data[vmodelKey])
            // 数据更新的同时重新解析模板
            // 这里使用观察者类观察数据变化所作出的响应
          })
        }
      }
      // 判断是否为文本节点,nodeType == 3
      // console.log(item.nodeType)
      // 如果是文本节点,进行数据替换
      // 如果不是文本节点,为元素节点则往里递归遍历文本节点
      if (item.nodeType === 3) {
        // 定义正则,替换{{xxx}}形式的字串为data下的属性值
        let reg = /\{\{(.*?)\}\}/g
        // 获取原本标签里的值,后续进行替换
        let text = item.textContent
        // console.log(text)
        item.textContent = text.replace(reg, (match, dataKey) => {
          // 先将dataKey去空格处理
          dataKey = dataKey.trim()
          // match为匹配到的整体,datakey为捕获到的子内容(.*?)
          //我们这里只需获取dataKey对应的值并塞入即可
          // console.log(match, dataKey)
          // 返回值作为替换内容 去除dataKey的前后空格

          // 增加观察者,传vue实例对象,data属性,item标签,标签属性
          // 相当于给每个文本节点都添加了一个观察者
          // 将所有观察者收集到vue实例上,在数据发生变化时调用观察者的update方法
          let watcher = new Watcher(this, dataKey, item, 'textContent')
          // 先进行判断观察者群组里是否有该节点的观察者
          // 如果有,就push添加,因为一个dataKey可能有多个模板使用
          // 举个例子,name属性可能在div1里使用也在div2里使用
          // 也就是将多个文本节点与同个datakey绑定
          if (this.$allWatcher[dataKey]) {
            this.$allWatcher[dataKey].push(watcher)
          }
          // 如果没有该属性的观察者存在,则新建空数组,push该观察者进入
          else {
            this.$allWatcher[dataKey] = []
            this.$allWatcher[dataKey].push(watcher)
          }
          return this.$data[dataKey]
        })
      }
    })
  }
  observe() {
    console.log('开始劫持')
    // 遍历所有的key,对其data数据劫持,值增加响应式功能
    for (let key in this.$data) {
      // 先获取value,否则数据重新定义后值会丢失
      // 此处的value变量不会随着observe方法的结束而销毁
      // 与内部匿名函数get和set作为闭包永远绑定在一起
      // 同时value值是对$data的一个引用,修改value值会引起$data变化
      let value = this.$data[key]
      // 保存一份vue的引用_this=this,
      // 防止后续在组件外部,也就是input输入框
      // 此时触发的set为一个闭包环境,上下文变成由defineproper定义的this.$data数据对象
      // 此时找不到vue实例作为上下文,对key和其他数据的引用也会失效
      let _this = this
      Object.defineProperty(this.$data, key, {
        get() {
          console.log('有人要获取劫持数据值', value)
          // 返回上面存储的value值
          // 由于是响应式的,只有当观察到数据变化时所以才接触数据
          // 其value值作用域也作用在劫持过程中
          return value
        },
        set(newValue) {
          console.log('劫持到数据,修改值为', newValue)
          console.log('劫持前的数据为', value)
          value = newValue
          // 更新值的同时进行模板更新
          // 由于观察者队列含有观察者来观察不同属性管理的若干个模板
          // 调用该属性值下所有模板观察者即可,
          // 只要属性值变化,该属性值下的所有观察者重新渲染模板
          console.log(_this.$allWatcher)
          console.log(_this.$allWatcher[key])
          _this.$allWatcher[key].forEach((watcher, index) => {
            watcher.update()
          })
        },
      })
    }
    console.log('劫持成功')
  }
}

class Watcher {
  constructor(vm, key, node, attr) {
    this.vm = vm
    this.key = key
    this.node = node
    this.attr = attr
  }
  //  item.textContent = this.$data[dataKey.trim()]
  update() {
    console.log('开始渲染')
    // 将原始dom标签内容值替换为 data里的属性值
    this.node[this.attr] = this.vm[this.key]
  }
}

代码参考

VUE双向绑定原理分析~实现视图和数据的双向绑定~_哔哩哔哩_bilibili

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

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

相关文章

集成学习(一)Bagging

前边学习了&#xff1a;十大集成学习模型&#xff08;简单版&#xff09;-CSDN博客 Bagging又称为“装袋法”&#xff0c;它是所有集成学习方法当中最为著名、最为简单、也最为有效的操作之一。 在Bagging集成当中&#xff0c;我们并行建立多个弱评估器&#xff08;通常是决策…

52-3 权限维持 - IFEO注入(镜像劫持)

IFEO注入(映像劫持)介绍 IFEO(Image File Execution Options)位于Windows注册表中的路径为: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options IFEO最初设计用于为在默认系统环境下可能出现错误的程序提供特殊的调试和执…

JavaWeb开发之环境准备-大合集

本文博客地址 JavaWeb开发 || 环境准备 1. 前言2. JDK8安装2.1 下载地址2.2 安装配置图示2.2.1 JDK安装2.2.2 配置系统环境变量 3. Maven安装3.1 Maven下载3.2 Maven解压及系统变量配置 4. Tomcat安装4.1 Tomcat下载4.2 Tomcat解压及系统变量配置 5. Redis安装5.1 Redis下载5.…

【postgresql】 基础知识学习

PostgreSQL是一个高度可扩展的开源对象关系型数据库管理系统&#xff08;ORDBMS&#xff09;&#xff0c;它以其强大的功能、灵活性和可靠性而闻名。 官网地址&#xff1a;https://www.postgresql.org/ 中文社区&#xff1a;文档目录/Document Index: 世界上功能最强大的开源…

顶顶通呼叫中心中间件-外呼通道变量同步到坐席通道变量(mod_cti基于Freeswitch)

机器人伴随转人工或者排队转人工 把外呼通道同步到坐席通道变量 在拨号方案转人工动作cti_acd,或者转机器人动作cti_rotobt的前面&#xff0c;添加一个 export nolocal:变量名${变量名} 一、配置拨号方案 win-ccadmin配置方法 点击拨号方案 -> 点击进入排队 -> 根据图…

liunx文件系统,日志分析

文章目录 1.inode与block1.1 inode与block概述1.2 inode的内容1.3 文件存储1.4 inode的大小1.5 inode的特殊作用 2.硬链接与软链接2.1链接文件分类 3.恢复误删除的文件3.1 案例:恢复EXT类型的文件3.2 案例:恢复XFS类型的文件3.2.1 xfsdump使用限制 4.分析日志文件4.1日志文件4.…

Ant Design Pro修改菜单栏的颜色

一&#xff1a;修改菜单栏的配色 第一步&#xff1a; 修改defaultSetting--> ProLayoutProps 中的属性值 sider: {colorMenuBackground: #fff, // menu 的背景颜色colorBgMenuItemHover: #91d5ff, // menuItem 的 hover 背景颜色colorTextMenu: #607AAF, // menuItem 的字…

智能与伦理:Kimi与学术道德的和谐共舞

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 Kimi&#xff0c;由月之暗面科技有限公司开发的智能助手&#xff0c;擅长中英文对话&#xff0c;能处理多种文档和网页内容。在论文写作中&#xff0c;Kimi可提供资料查询、信息整理、语…

2024年【金属非金属矿山(地下矿山)安全管理人员】考试报名及金属非金属矿山(地下矿山)安全管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员考试报名为正在备考金属非金属矿山&#xff08;地下矿山&#xff09;安全管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的金属非…

KVM虚机调整磁盘大小(注:需重启虚拟机)

1、将磁盘大小由15G调整为25G [rootkvm ~]# virsh domblklist kvm-client #显示虚拟机硬盘列表 [rootkvm ~]# qemu-img resize /var/lib/libvirt/images/tesk-disk.qcow2 10G #扩容 [rootkvm ~]# qemu-img info /var/lib/libvirt/images/test-disk.qcow2 #查看信息 注&…

【论文通读】A Survey of Neural Code Intelligence: Paradigms, Advances and Beyond

A Survey of Neural Code Intelligence: Paradigms, Advances and Beyond 前言摘要代码模型的发展神经语言建模代码预训练模型大型语言模型 学习范式的转移数据集与评测基准跨领域协同应用与未来资源阅读总结 前言 神经代码智能利用深度学习理解、生成和优化代码&#xff0c;连…

3D地图是智慧城市可视化项目绕不开的技术!来我帮你解决

**3D地图&#xff1a;智慧城市可视化项目绕不开的技术&#xff01;来我帮你解决** 智慧城市已成为未来城市发展的必然趋势。而3D地图作为智慧城市可视化项目的核心技术之一&#xff0c;其重要性不言而喻。本文将深入探讨3D地图在智慧城市建设中的应用及其优势&#xff0c;为您…

待研究课题记录

最近了解到两个新的有趣的节点&#xff0c;但是对于实际效果不是很确定&#xff0c;所以这里记录下&#xff0c;后续慢慢研究&#xff1a; 扰动注意力引导 Perturbed Attention Guidance GitHub - KU-CVLAB/Perturbed-Attention-Guidance: Official implementation of "…

Python 游戏服务器架构优化

优化 Python 游戏服务器的架构涉及多个方面&#xff0c;包括性能、可伸缩性、并发处理和网络通信。下面是一些优化建议&#xff1a; 1、问题背景 在设计 Python 游戏服务器时&#xff0c;如何实现服务器的横向扩展&#xff0c;以利用多核处理器的资源&#xff0c;并确保服务器…

商品分页,商品模糊查询

一、商品分页 引入分页 定义分页主件的参数 在请求url上拼接参数 定义改变当前页码后触发的事件&#xff0c;把当前页码的值给到分页表单&#xff0c;重新查询 二、商品查询&#xff08;以商品的名称查询name为例&#xff09; 引入elementplus的from表单组件 定义一个FormData…

DDR的拓扑与仿真

T型拓扑 vs Fly-by 由于T型拓扑在地址、命令和时钟都是同时到达每个DDR芯片&#xff0c;所以同步的切换噪声会叠加在一起&#xff0c;DDR越多这个信号上叠加的噪声越大&#xff0c;T型拓扑的优点是地址、命令和时钟都是同时到达&#xff0c;所以不需要做写均衡Write leveling。…

Java面试八股之MYISAM和INNODB有哪些不同

MYISAM和INNODB有哪些不同 MyISAM和InnoDB是MySQL数据库中两种不同的存储引擎&#xff0c;它们在设计哲学、功能特性和性能表现上存在显著差异。以下是一些关键的不同点&#xff1a; 事务支持&#xff1a; MyISAM 不支持事务&#xff0c;没有回滚或崩溃恢复的能力。 InnoDB…

HCIA综合实验

学习新思想&#xff0c;争做新青年。今天学习的是HCIA综合实验&#xff01; 实验拓扑 实验需求 总部&#xff1a; 1、除了SW8 SW9是三层交换机&#xff0c;其他交换机均为2层交换机。 2、GW为总部的出口设备&#xff0c;使用单臂路由技术&#xff0c;VLAN10,20,100的网关都在GW…

leetcode力扣_排序问题

215.数组中的第K个最大元素 鉴于已经将之前学的排序算法忘得差不多了&#xff0c;只会一个冒泡排序法了&#xff0c;就写了一个冒牌排序法&#xff0c;将给的数组按照降序排列&#xff0c;然后取nums[k-1]就是题目要求的&#xff0c;但是提交之后对于有的示例显示”超出时间限制…

某乎X-Zse-96(xzse96)分析

特别声明&#xff01;本章只单纯为了学习&#xff01; 前言 今天通过知乎的一个参数&#xff0c;来学习逆向的新思路&#xff0c;希望大家能有所掌握。 一.抓包分析 我们进入之后搜索&#xff0c;发现这个数据包比较大&#xff0c;肯定就是这个了包了 然后我们就进去观看请…