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

效果

简述原理

配置对象传入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对象代理到$datathis.$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身上所有keyfor (let key in this.$data) {// 数据代理给vue实例,thisObject.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.valueconsole.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 = thisObject.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 = vmthis.key = keythis.node = nodethis.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.xdnf.cn/news/1473549.html

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

相关文章

django高校教务系统-计算机毕业设计源码81661

目 录 摘要 1 绪论 1.1 研究背景 1.2目的及意义 1.3论文结构与章节安排 2 高校教务系统设计分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4…

【云WAF为您的Web防御保驾护航】

在这个数字时代&#xff0c;网络就像是一张没有尽头的大网&#xff0c;将整个世界都联系在了一起。但是&#xff0c;在这个网络的背后&#xff0c;却潜藏着数不清的安全隐患。恶意攻击、数据泄漏、网站瘫痪……各种隐患就像是隐藏在暗处的毒蛇&#xff0c;时刻都会对没有任何防…

金蝶云苍穹-插件开发(一)加载数据

前言 此系列博客是进行金蝶云苍穹开发时的插件开发的教程&#xff0c;一是在明年要是还要参加软件杯金蝶A6赛题的话&#xff0c;可以看此系列教程的博客来进行复习&#xff0c;同时如果要是我实验室的学弟学妹要参加的话&#xff0c;我这个系列的博客可以给他们提供学习参考&a…

破解宇宙终极奥秘,战胜昊天无上束缚

在幽邃的暗夜下&#xff0c;细品着夫子与昊天跨越千年的智勇交锋&#xff0c;我的思绪不禁飘向了更加深远的宇宙边际&#xff0c;回响起那些关于人类如何挑战天命、战胜上天的过往。 宇宙奥秘 在浩瀚无垠的宇宙深渊中&#xff0c;隐藏着一段超越凡尘的规则。昊天&#xff0c;…

2024/7/6 英语每日一段

More than half of late-teens are specifically calling for more youth work that offers “fun”, with older teenagers particularly hankering for more jollity, according to a study carried out by the National Youth Agency. One in 10 said they have zero option…

【Go】excelize库实现excel导入导出封装(四),导出时自定义某一列或多列的单元格样式

大家好&#xff0c;这里是符华~ 查看前三篇&#xff1a; 【Go】excelize库实现excel导入导出封装&#xff08;一&#xff09;&#xff0c;自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头 【Go】excelize库实现excel导入导出封装&#xff08;二&…

AI Earth应用—— 在线使用sentinel数据VV和VH波段进行水体提取分析(昆明抚仙湖、滇池为例)

AI Earth 本文的主要目的就是对水体进行提取,这里,具体的操作步骤很简单基本上是通过,首页的数据检索,选择需要研究的区域,然后选择工具箱种的水体提取分析即可,剩下的就交给阿里云去处理,结果如下: 这是我所选取的一景影像: 详情 卫星: Sentinel-1 级别: 1 …

新产品或敏捷项目过程 SOP,附带流程图及流程规范

一、项目启动 项目背景和目标明确 市场调研结果分析&#xff0c;确定新产品的需求和市场机会。制定明确的项目目标&#xff0c;包括产品特性、上市时间、预期收益等。 组建项目团队 确定项目经理、产品经理、开发人员、测试人员、市场人员等角色。明确各成员的职责和权限。 项目…

Java语言程序设计基础篇(第10版)编程练习题13.18(使用 Rational 类)

第十三章第十八题(使用 Rational 类) 题目要求&#xff1a; 编写程序&#xff0c;使用 Rational 类计算下面的求和数列: 你将会发现输出是不正确的 &#xff0c;因为整数溢出(太大了)。为了解决这个问题 &#xff0c;参见编程练习題13.15。代码参考&#xff1a; package cha…

蜂窝物联农业气象站,守护丰收每一步

现代农业的革新者——农业自动气象站&#xff0c;正以其多功能的传感器、高效的数据采集传输系统、智能的数据云平台以及可靠的供电供网系统&#xff0c;成为农业生产中的得力助手。这些传感器能够实时监测温度、湿度、风速、风向、气压、土壤温度、土壤湿度、土壤PH值、土壤盐…

CorelDRAW2024新版本来咯!你的设计神助手

&#x1f389; 设计界的朋友们&#xff0c;注意啦&#xff01;你们的新宠——CorelDRAW 2024 来咯&#xff01; &#x1f31f; 一、设计神器再进化 亲爱的设计小伙伴们&#xff0c;有没有感觉每天与那些不配合的软件战斗&#xff0c;像是在打怪升级&#xff1f;&#x1f409; …

Selenium的这些自动化测试技巧你知道几个?

Selenium自动化测试技巧 与以前瀑布式开发模式不同&#xff0c;现在软件测试人员具有使用自动化工具执行测试用例套件的优势&#xff0c;而以前&#xff0c;测试人员习惯于通过测试脚本执行来完成测试。 但自动化测试的目的不是完全摆脱手动测试&#xff0c;而是最大程度地减少…

js的作用域链

function test(){} 运行期上下文&#xff1a;当函数执行时&#xff0c;会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境&#xff0c;函数每次执行时对应的执行上下文都是 独一无二的&#xff0c;所以多次调用一个函数对导致创建多个执行上下…

leetcode力扣_排序问题

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

谷粒商城学习笔记-12-开发工具环境安装配置

文章目录 一&#xff0c;JDK的安装1.进入官网下载2&#xff0c;jdk安装3&#xff0c;Java环境验证 二&#xff0c;Idea安装插件1&#xff0c;lombok2&#xff0c;MybatisX 三&#xff0c;Maven的下载与配置1&#xff0c;下载Maven2&#xff0c;Idea配置Maven3&#xff0c;maven…

ESP32CAM物联网教学05

ESP32CAM物联网教学05 超级监控摄像头 点灯科技APP还能查看摄像头的视频呢&#xff01;这样&#xff0c;小智的物联网小车就变身成为超级监控摄像头啦。 测试摄像头视频图像 我们把前面的物联网小车的程序稍作修改&#xff0c;增加了摄像头的程序&#xff0c;去掉了按键组件程…

RocketMQ-订阅一致及解决方案

背景 这里借用Rocketmq官方的一句话来描述订阅关系一致: 订阅关系一致指的是同一个消费者分组Group ID下&#xff0c;所有Consumer实例所订阅的Topic和Tag必须完全一致。如果订阅关系不一致&#xff0c;可能导致消息消费逻辑混乱&#xff0c;消息被重复消费或遗漏。 具体的问题…

关于GIS的概念方面在前端编程中的理解

关于GIS的概念方面在前端编程中的理解 一. 什么是gis二. 关于地球的建模(了解)三. GIS坐标系表现形式四.GIS的数据4.1 矢量数据4.2 栅格数据4.3 矢量数据和栅格数据的不同 一. 什么是gis 地理坐标系统&#xff0c;其目的就是通过地理坐标系可以确定地球上任何一点的位置。 二. …

jenkins配置gitee源码地址连接不上

报错信息如下&#xff1a; 网上找了好多都没说具体原因&#xff0c;最后还是看jenkins控制台输出日志发现&#xff1a; ssh命令执行失败&#xff08;git环境有问题&#xff0c;可能插件没安装成功等其他问题&#xff09; 后面发现是jenkins配置git的地方git安装路径错了。新手…

215. 数组中的第K个最大元素(中等)

215. 数组中的第K个最大元素 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;215. 数组中的第K个最大元素 2.详细题解 快速排序算法在每一轮排序中&#xff0c;随机选择一个数字 x x x&#xff0c;根据与 x x x的大小关系将要排序的数…