class03:MVVM模型与响应式原理
目录
- 一、MVVM模型
- 二、内在
- 1. 深入响应式原理
- 2. Object.entries
- 3. 底层搭建
一、MVVM模型
MVVM,即Model 、View、ViewModel。
Model => data数据
view => 视图(vue模板)
ViewModel => vm => vue 返回的实例 => 控制中心, 负责监听Model的数据,进行改变并且控制视图的更新
vue渲染流程
1. vue 拿到挂载中的所有节点
2. vue 取data中的数据,插入到带有vue指令,特殊的vue符号中
3. vue 将数据插入成功的元素放回到页面中
二、内在
1. 深入响应式原理
如何追踪变化: 当把一个普通的 JavaScript 对象传入 Vue 实例作为 data
选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty把这些 property 全部转为 getter/setter。
例:监听对象的变化
<script>
// 方法:Object.defineProperty(监听对象,对象的属性名,{配置对象})
let data = {age:38,
}
// 定义一个变量
let _data = null;
Object.defineProperty(data,"age",{get(){console.log("get--data.age取值的时候触发");return _data},set(val){console.log("set--设置data.age的时候触发");_data = val}
})
</script>
使用Object.defineProperty方法,函数内部把属性转化为get/set,get()函数在函数取值时触发,set()函数在设值时触发,通过在外部定义变量,在get()函数内部返回该变量,在set()内部将设的值赋值给该外部变量,从而实现监听。
对象之间的关联:原对象与代理对象
<script>// 关联:代理 let data = {age: 38,}// 定义一个变量let _data = {};Object.defineProperty(_data, "age", {get() {return data.age},set(val) {data.age = val}})
</script>
如上述代码,监听的对象是外部定义的对象,监听的属性名是另一个对象中的属性。在get()执行时,将data.age的值返回给监听对象_data,那么 _data中就会生成一个属性值age: 38,同理在set()执行时,也会将设置的值返回给监听对象 _data,从而修改 _data中的属性值。两个对象data与 _data之间是代理的关系。
vue2底层:数据代理 => 通过一个对象代理另一个对象的中的属性操作(读/写)
2. Object.entries
Object.entries将一级对象处理成键值对的形式。
let data = {name:"Evan You",age:"36",sex:"man"
}
然后通过循环遍历,使用Object.defineProperty方法,把属性转化为get/set,最后代理给_data。
// 原对象
let data = {name:"Evan You",age:36,sex:"man"
}
// 代理对象
let _data = {}
// 处理键值对
Object.entries(data).forEach(([key, val]) => {// 获取data对象的键值对,交给_data代理 createObj(_data, key, val)
})
// 代理
function createObj(_data, key, val){console.log(_data, key, val);Object.defineProperty(_data, key, {// 对data的每一个属性key,get获取值,set将值赋给属性,最后将属性及其对应值赋给监听对象_dataget(){return val;},set(value){val = value;}})
}
修改_data的值,但data的值不会受影响。
3. 底层搭建
创建一个class类Test,返回一个constructor对象,实例化Test。在constructor输出arguments可获得节点和数据。
class Test{constructor(){console.log(arguments);}
}
let vm = new Test({el:"#root",data(){return {num:"32",name:"Jordan",country:"Ame",work:"basketball"}}
})
操作:
<div id="root">{{name}</br><input type="text" v-model="name"> </br>{{work}}
</div>
vue底层:
- vue 拿到挂载中的所有节点;
- vue 拿data中的数据, 插入到带有vue指令,特殊的vue符合中。Object.defineProperty监听data数据;
- vue 将数据插入成功的元素放回到页面中。
class Test {constructor({el, data}) { // 解构赋值// 获取节点this.el = document.querySelector(el);this._data = data;// 调用方法getDom(this.el, this._data)}
}
// 获取节点
function getDom(node, _data){console.log(node, _data);
}
可以通过firstChild和nextSibling获取每一个节点:
getDom函数:
function getDom(node, _data){// console.log(node, _data);// 文本代码拼凑,创建一个新的空白的文档片段let frame = document.createDocumentFragment();let child;console.log(node.firstChild);// 空循环,将赋值给childwhile(child = node.firstChild){// 插入到frameframe.append(child);console.log(child);// child获得节点}return frame;
}
执行结果:页面上的节点被剪切。
循环过程:每执行一次append操作,root的第一个节点就会被剪切掉,当所有节点被剪切掉时为null循环结束。
接下来,我们在进入循环之后先调用另一个函数getDom2,判断节点的类型并把原对象的数据取出来赋值给这些节点。
function getDom(node, _data){let frame = document.createDocumentFragment();while(child = node.firstChild){getDom2(child, _data)frame.append(child); }// 返回操作后的节点return frame;
}function getDom2(node, _data){console.log(node, node.nodeType);//正则 匹配插值符号{{}}let reg = /\{\{(.*)\}\}/// 节点// if(noede.nodeType == 1){ // 元素节点,nodeType 属性返回 1 // }if(node.nodeType == 3){ //文本节点,nodeType 属性返回 3// 如果该文本节点匹配到{{}},返回trueif(reg.test(node.nodeValue)){ // 取出节点值,匹配{{}}console.log(reg.test(node.nodeValue)); // 文本节点,返回trueconsole.dir(RegExp);// $1为RegExp的属性,获取{{}}里面的值-> name,worklet arg = RegExp.$1.trim();// 获取{{}}里面的值-> name,workconsole.log(arg);// 将原对象的name,work及其值赋给页面中的{{name}},{{work}}node.nodeValue = _data[arg];// 节点分别为:Jordan、basketballconsole.log(node);// 将数据(节点 data)存储下来 new watcher(_data, node, arg)}}
}class Test {constructor({el, data}) {this.el = document.querySelector(el);this._data=data;// 获取节点this.dom = getDom(this.el, this._data)// 将返回的节点插入页面this.el.append(this.dom)}
}
输出说明:
**说明:**为了方便,html的div中将用于换行的两个删去。
对于元素节点,进行下述处理。
if(node.nodeType == 1){ // 元素节点,nodeType 属性返回 1 console.log(node, node.nodeType);// 获取元素节点上的属性节点console.log(node.attributes);[...node.attributes].forEach((item) => { // 遍历属性节点if(item.nodeName == "v-model"){console.log(item.nodeName);// 获取v-model的属性值-> namelet arg = item.nodeValue;// 双向数据绑定,通过页面修改原对象node.addEventListener("input",(ev)=>{_data[arg] = ev.target.value})console.log(arg);// 将原对象data中的name赋值(代理)到v-model的namenode.value = _data[arg];console.log(node.value);console.log(node);// 将数据(节点 data)存储下来 new watcher(_data, node, arg)}})
}
从vue底层原理可知,在获取节点以及渲染之前,应该先进行数据监听。
数据监听第一步是启动订阅(Dep类),然后调用Object.defineProperty所有属性转为get/set,在get中调用Dep类的addSub方法存储数据,在set中调用Dep类的notify方法修改数据。此时还未获取节点,属于在后端修改数据。
在获取节点的分类节点并插入页面后,调用watcher类,该类先保存数据,并传送数据给Dep类,也进行数据的获取和修改。此时以获取节点,属于在页面修改数据。
以上两部分即是双向数据绑定。
// 订阅发布:在数据变动时,发给订阅者,触发对应的函数
class Dep {constructor() {// 保留数据this.sub = []}addSub(val) {this.sub.push(val)}notify() {this.sub.map((item) => {item.update()})}
}//观察者 => 保存数据,以便后期进行修改
class watcher {constructor(_data, node, arg) {// 数据也给Dep一份,以便Dep订阅数据是否变化Dep.target = this// 保存数据this._data = _data;this.node = node;this.arg = arg;this.init()}init() {// 用于后期进行数据修改this.update()// 修改数据后,清空Dep留存的数据Dep.target = null}update() {// 用于获取数据this.get()// 修改数据this.node.value = this.node.nodeValue = this.value}get() {// 获取数据,数据代理this.value = this._data[this.arg]}
}//处理数据监听
function setdefineProperty(data, _data) { // data===_dataObject.entries(data).forEach(([key, val]) => {createObj(_data, key, val)});
}//监听数据
function createObj(_data, key, val) {//启动 订阅发布let dep = new Dep()// 所有属性转为get/setObject.defineProperty(_data, key, {get() {// 如果Dep.target 有数据if (Dep.target) { //没有数据不就要放进去 dep.addSub(Dep.target)}return val},set(value) {// 数据相同不作改变if (val == value) {return}// 更改数据val = value;// 设置的时候修改页面数据dep.notify()}})
}
双向数据绑定需在获取输入框节点的代码中加入:
// 双向数据绑定,通过页面修改原对象
node.addEventListener("input", (ev) => {_data[arg] = ev.target.value
})
最后,最好将多余的属性和样式删除。如删除v-model。
// 删除属性
node.removeAttribute("v-model")
相关文章:

class03:MVVM模型与响应式原理
目录一、MVVM模型二、内在1. 深入响应式原理2. Object.entries3. 底层搭建一、MVVM模型 MVVM,即Model 、View、ViewModel。 Model > data数据 view > 视图(vue模板) ViewModel > vm > vue 返回的实例 > 控制中心, 负责监听…...

[Spring学习]08 @Resource和@Autowired注解的区别
目录前言一、Resource和Autowired注解的身世1、Resource注解2、Autowired注解3、常见的三种依赖注入方式及区别1. Filed注入2. Setter注入3. Constructor注入4. 三种依赖注入方式的区别二、Resource和Autowired注解的区别三、Resource和Autowired注解的推荐用法前言 当我们在属…...

前端开发神器VS Code安装教程
✅作者简介:CSDN一位小博主,正在学习前端 📃个人主页:白月光777的CSDN博客 💬个人格言:但行好事,莫问前程 安装VS CodeVS Code简介VS Code安装VS Code汉化结束语💡💡&…...

【Hive进阶】-- Hive SQL、Spark SQL和 Hive on Spark SQL
1.Hive SQL 1.1 基本介绍概念Hive由Facebook开发,用于解决海量结构化日志的数据统计,于2008年贡献给 Apache 基金会。Hive是基于Hadoop的数据仓库工具,可以将结构化数据映射为一张表,提供类似SQL语句查询功能本质:将Hi…...

搭建自己的直播流媒体服务器SRS,以及SRS+OBS直播推拉流使用及配置
一、前言 目前,全球直播带货什么的,成为主流,那如何自己搭建一个直播服务器呢。首先需要一个流媒体服务器,搭建流媒体有很多种方式,如下: 流媒体解决方案 Live555 (C)流媒体平台框…...

Node.js-----使用express写接口
使用express写接口 文章目录使用express写接口创建基本的服务器创建API路由模块编写GET接口编写POST接口CROS跨域资源共享1.接口的跨域问题2.使用cros中间件拒绝跨域问题3.什么是cros4.cros的注意事项5.cros请求的分类JSONP接口1.回顾jsonp的概念和特点2.创建jsonp接口的注意事…...

【Linux修炼】16.共享内存
每一个不曾起舞的日子,都是对生命的辜负。 共享内存一.共享内存的原理二.共享内存你的概念2.1 接口认识2.2演示生成key的唯一性2.3 再谈key三.共享资源的查看3.1 如何查看IPC资源3.2 IPC资源的特征3.3 进程之间通过共享内存进行关联四.共享内存的特点五.共享内存的内…...

JAVA进阶 —— Stream流
目录 一、 引言 二、 Stream流概述 三、Stream流的使用步骤 1. 获取Stream流 1.1 单列集合 1.2 双列集合 1.3 数组 1.4 零散数据 2. Stream流的中间方法 3. Stream流的终结方法 四、 练习 1. 数据过滤 2. 数据操作 - 按年龄筛选 3. 数据操作 - 演员信息要求…...

Linux基础命令大全(上)
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...

嵌入式 串口通信
目录 1、通信的基本概念 1.1 串行通信 1.2 并行通信 2、串行通信的特点 2.1 单工 2.2 半双工 2.3 全双工 3、串口在STM32的引脚 4、STM32的串口的接线 4.1 STM32的串口1和电脑通信的接线方式 4.2 单片机和具备串口的设备连接图 5、串口通信协议 6、串口通信…...

C语言函数调用栈
栈溢出(stack overflow)是最常见的二进制漏洞,在介绍栈溢出之前,我们首先需要了解函数调用栈。 函数调用栈是一块连续的用来保存函数运行状态的内存区域,调用函数(caller)和被调用函数…...

【高阶数据结构】红黑树
文章目录1. 使用场景2. 性质3. 结点定义4. 结点旋转5. 结点插入1. 使用场景 Linux进程调度CFSNginx Timer事件管理Epoll事件块的管理 2. 性质 每一个节点是红色或者黑色根节点一定是黑色每个叶子节点是黑色如果一个节点是红色,那么它的两个儿子节点都是黑色从任意…...

网络协议分析期末复习(二)
目录 12. 端口的定义及常见应用对应的端口号 13. UDP协议概述 14.UDP数据报格式及各字段意义 15. UDP-Lite协议概述 16. TCP数据报格式及各字段意义 17. TCP连接建立及协商参数的过程 18. TCP连接释放过程 19. 路由协议分类及各类的具体协议 20. 路由算法常用的度量 2…...

【C++】STL简介 及 string的使用
文章目录1. STL简介1.1 什么是STL1.2 STL的版本1.3 STL的六大组件2. string类的使用2.1 C语言中的字符串2.2 标准库中的string类2.3 string类的常用接口说明1. string类对象的常见构造2. string类对象的容量操作3. string类对象的修改操作4. resize和reserve5. 认识迭代器&…...

MySQL事务详解
🏆今日学习目标: 🍀Spring事务和MySQL事务详解 ✅创作者:林在闪闪发光 ⏰预计时间:30分钟 🎉个人主页:林在闪闪发光的个人主页 🍁林在闪闪发光的个人社区,欢迎你的加入: …...

ChatGPT背后的技术和多模态异构数据处理的未来展望——我与一位资深工程师的走心探讨
上周,我和一位从业三十余年的工程师聊到ChatGPT。 作为一名人工智能领域研究者,我也一直对对话式大型语言模型非常感兴趣,在讨论中,我向他解释这个技术时,他瞬间被其中惊人之处所吸引🙌,我们深…...

iOS-砸壳篇(两种砸壳方式)
CrackerXI砸壳呢,当时你要是使用 frida-ios-dump 也是可以的; https://github.com/AloneMonkey/frida-ios-dump frida-ios-dump: 代码中需要更改的:手机中的内网ip 密码 等 最后放到我的砸壳路径里: python dump.py -l查看应用…...

linux 基础
1.Shell 命令的格式如下:command -options [argument]command: Shell 命令名称。options: 选项,同一种命令可能有不同的选项,不同的选项其实现的功能不同。argument: Shell 命令是可以带参数的,也可以不带参…...
Java:SpringBoot给Controller添加统一路由前缀
网上的文章五花八门,不写SpringBoot的版本号,导致代码拿来主义不好使了。 本文采用的版本 SpringBoot 2.7.7 Java 1.8目录1、默认访问路径2、整个项目增加路由前缀3、通过注解方式增加路由前缀4、按照目录结构添加前缀参考文章1、默认访问路径 packag…...

Java 基于 JAVE 库 实现 视频转音频的批量转换
文章目录 Java 基于 JAVE 库 实现 视频转音频的批量转换Maven:方案一:代码优化:方案二:示例代码:代码优化:结语Java 基于 JAVE 库 实现 视频转音频的批量转换 实现视频转音频的功能需要使用到一个第三方的 Java 库,叫做 JAVE。JAVE 是一个开源的 Java 库,提供了视频和音频转换…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...