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.mzph.cn/web/41354.shtml

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

相关文章

sqlite 数据库 介绍

文章目录 前言一、什么是 SQLite &#xff1f;二、语法三、SQLite 场景四、磁盘文件 前言 下载 目前已经出到了&#xff0c; Version 3.46.0 SQLite&#xff0c;是一款轻型的数据库&#xff0c;是遵守ACID的关系型数据库管理系统&#xff0c;它包含在一个相对小的C库中。它是…

VMware虚拟机配置桥接网络

转载&#xff1a;虚拟机桥接网络配置 一、VMware三种网络连接方式 VMware提供了三种网络连接方式&#xff0c;VMnet0, VMnet1, Vmnet8&#xff0c;分别代表桥接&#xff0c;Host-only及NAT模式。在VMware的编辑-虚拟网络编辑器可看到对应三种连接方式的设置&#xff08;如下图…

美好生活的 100 条建议

简介 一些简洁明了的人生建议&#xff0c;易于理解&#xff0c;并且能够为日常生活中的各个方面提供实用的指导。 财富 (Possessions) 1. If you want to find out about people’s opinions on a product, google \reddit. You’ll get real people arguing, as compared t…

SpringBoot2.2.6使用spring-boot-validation读取不到自定义配置文件中的属性

SpringBoot2.2.6没有做message.properties文件中属性的自动读取配置。解决方法有两种&#xff1a; 1. 升级springboot版本到2.6.x以上 2. 在现有springboot版本的基础上添加以下自定义配置&#xff1a; Configuration public class RequestParamValidationConfig implements…

学习笔记——交通安全分析12

目录 前言 当天学习笔记整理 4信控交叉口交通安全分析 结束语 前言 #随着上一轮SPSS学习完成之后&#xff0c;本人又开始了新教材《交通安全分析》的学习 #整理过程不易&#xff0c;喜欢UP就点个免费的关注趴 #本期内容接上一期11笔记 当天学习笔记整理 4信控交叉口交…

ORA-03115 ORA-06594--空间不足 rman 磁带压缩备份 控制文件恢复后备份信息丢失

---用旧的控制文件恢复后 这个控制文件本身的备份信息不在此还原出的控制文件中。所以这个备份的控制文件就删不掉&#xff0c;看不到。 但是只要设置正确的dbid&#xff0c;还是可以用恢复这个控制文件的 正常未恢复过controlfile的话&#xff0c;这个控制文件备份信息在现有…

Square Root SAM论文原理

文章目录 Square Root SAM论文原理核心原理SLAM问题的3种表示贝叶斯网络因子图&#xff08;Factor graph&#xff09;马尔科夫随机场(Markov Random Field, MRF) SLAM最小二乘问题&线性化因式分解 factorization矩阵与图(Matrices ⇔ Graphs)因式分解&变量消元(Factori…

Kafka系列之Kafka知识超强总结

一、Kafka简介 Kafka是什么 Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff08;消息引擎系统&#xff09;&#xff0c;它可以处理消费者在网站中的所有动作流数据。 这种动作&#xff08;网页浏览&#xff0c; 搜索和其他用户的行动&#xff09;是在现代网络上的许多社…

14-22 剑和远方2 - 深度神经网络中的学习机制

概论 在第一部分中&#xff0c;我们深入探讨了人工智能的兴衰简史以及推动人工智能发展的努力。我们研究了一个简单的感知器&#xff0c;以了解其组件以及简单的 ANN 如何处理数据和权重层。在简单的 ANN 中&#xff0c;不会对数据执行特定操作。ANN 中的激活函数是一个线性函…

Surface splatting (2D Gaussian splatting)代码分析

源码地址 colab.research.google.com/drive/1qoclD7HJ3-o0O1R8cvV3PxLhoDCMsH8W 核心代码 surface_splatting def surface_splatting(means3D, scales, quats, colors, opacities, intrins, viewmat, projmat):# Rasterization setupprojmat torch.zeros(4,4).cuda()projm…

flask使用定时任务flask_apscheduler(APScheduler)

Flask-APScheduler描述: Flask-APScheduler 是一个 Flask 扩展&#xff0c;增加了对 APScheduler 的支持。 APScheduler 有三个内置的调度系统可供您使用&#xff1a; Cron 式调度&#xff08;可选开始/结束时间&#xff09; 基于间隔的执行&#xff08;以偶数间隔运行作业…

c#中的超时终止

在C#中&#xff0c;可以使用CancellationToken和Task的超时机制来实现调用方法时的超时终止。 一 用Task.Delay(int)模拟耗时操作 static async Task Main(string[] args){using (var cts new CancellationTokenSource(1 * 1000)){await doSomething(cts.Token);}Console.Wr…

移动校园(7)ii:uniapp响应拦截器处理token,以及微信小程序报错当前页面正在处于跳转状态,请稍后再进行跳转....

依据昨天的写完&#xff0c;在token过期之后&#xff0c;再次调用接口&#xff0c;会触发后端拦截&#xff0c;扔进全局错误处理中间件 前端说明提示都没有&#xff0c;只有一个这个&#xff0c;现在优化一下&#xff0c;再写一个类似全局后置守卫&#xff0c;当状态码是401的时…

RAID 冗余磁盘阵列

RAID也是Linux操作系统中管理磁盘的一种方式。 只有Linux操作系统才支持LVM的磁盘管理方式。 而RAID是一种通用的管理磁盘的技术&#xff0c;使用于多种操作系统。 优势&#xff1a;提升数据的读写速度&#xff0c;提升数据的可靠性。具体实现哪什么功能&#xff0c;要看你所…

RGB树-美团2023笔试(codefun2000)

题目链接 RGB树-美团2023笔试(codefun2000) 题目内容 塔子哥是一位著名的冒险家&#xff0c;他经常在各种森林里探险。今天&#xff0c;他来到了道成林&#xff0c;这是一片美丽而神秘的森林。在探险途中&#xff0c;他遇到了一棵 n 个节点的树&#xff0c;树上每个节点都被涂…

LVGL移植与VS模拟器使用

一、移植文件介绍 二、移植部分 第一步&#xff1a;创建LVGL文件夹 第二步&#xff1a; 构造LVGL文件夹&#xff1a;LVGL - GUI - lvgl - 第三步&#xff1a;添加文件 3.1 从examples中添加2个.c文件 3.2 从src中添加文件 draw文件 extra文件 第四步&#xff1a; 三、Ke…

Linux系统安装软件包的方法rpm和yum详解

起因&#xff1a; 本篇文章是记录学习Centos7的历程 关于rpm 常见命令 1&#xff09;查看已经安装的软件包 rpm -q 软件包名 2&#xff09;查看文件的相关信息 rpm -qi 软件包名 3&#xff09;查看软件包的依赖关系 就是说要想安装这个软件包&#xff0c;就必须把一些前…

三级_网络技术_04_中小型网络系统总体规划与设计

1.下列关于路由器技术特征的描述中&#xff0c;正确的是()。 吞吐量是指路由器的路由表容量 背板能力决定了路由器的吞吐量 语音、视频业务对延时抖动要求较低 突发处理能力是以最小帧间隔值来衡量的 2.下列关于路由器技术特征的描述中&#xff0c;正确的是()。 路由器的…

springboot公寓租赁系统-计算机毕业设计源码03822

摘要 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 系…

韦东山嵌入式linux系列-第一个实验

1 前言 笔者使用的是韦东山STM32MP157 Pro的板子&#xff0c;环境搭建部分按照说明文档配置完成。配置桥接网卡实现板子、windows、ubuntu的通信&#xff0c;也在开发板挂载 Ubuntu 的NFS目录 &#xff0c;这里就不再赘述了。 板子: 192.168.5.9 windows: 192.168.5.10 ubunt…