Vue响应式原理

Vue响应式原理

1 目的

Vue 通过数据劫持获取数据变化,利用发布-订阅模式,在当数据发生改变时自动响应到界面上。

2 原理

  1. **Obeserve 建立数据劫持(观察)。**通过js的Object.defineProperty(vue3使用Proxy)监听劫持数据对象的每一个属性的Getter和Setter(如果属性是对象,对于内部的修改将无法监听到,如何需要深度监听每一个对象可以使用递归遍历每一个对象类型的属性建立Getter和Setter,实现深度监听)。
  2. **Compile 编译和解析 html 模板。**将界面引用的变量进行替换并通过添加订阅者Watcher与界面建立关系,将建立的Watcher添加到对于数据的Dep(属性订阅器:就是发布订阅模式的中间器代理,内部维护着所有绑定此属性所有的Watcher,当属性发生改变可以统一一起通知Watcher进行界面修改)。Watcher中必须有一个update函数用于通知更新界面。
  3. **响应式过程。**当数据发生改变时,通过Obeserve 中Setter获取到改动动作和新数据,通过Dep属性订阅器通知每一个订阅者Watcher更新界面。Vue的MVVM模型便是包含Obeserve(观察的数据) 、Compile(解析模板将变量替换成数据) 、Watcher(建立Obeserve与Compile关系,实现依赖收集,统一更新)。

3 实现

Obeserve 数据劫持

/*** 实现数据监听* 使用Object.defineProperty实现响应式坏处:* 1.无法监听对象属性的添加、删除(可以使用vue中$set)* 2.对于数组无法监听下标变化,所以通过下标添加属性无法监听到(vue内部做了优化数组常用的操作函数如* push/pop/shift/unshift/splice/sort/reverse可以响应)* @param obj* @param deep 深度监听*/
function observe(obj,deep=false) {if (obj==null||typeof obj !== 'object'){console.log('not is a object')return}for (let objKey in obj) {const value=obj[objKey]if (deep&&value!=null&&typeof value ==='object'){observe(value)}defineObserve(obj,objKey,value)}
}function defineObserve(obj,key,val) {Object.defineProperty(obj,key,{get() {//可以在这里实现收集订阅者,因为Compile需要获取数据,必然需要调用Getterreturn val},set(v) {//新旧相同 passif (val === v){return}//可以在这里调用Dep中所有Watcher的update通知所有订阅界面更新console.log('update view')//更新数据val = v}})
}/*** vue3* 使用 ES6新特性 Proxy实现响应式:* 1.可以劫持对象、数组的元素的添加和删除;* 2.需要使用new操作符,并返回一个新的Proxy对象。* @param obj* @param deep*/
function observeVue3(obj,deep=false) {if (obj==null||typeof obj !== 'object'){console.log('not is a object')return}if (deep){for (const objKey in obj) {const value=obj[objKey]if (value&&typeof value === 'object'){obj[objKey]=observeVue3(value,deep)}}}return new Proxy(obj,{get(target, p, receiver) {return Reflect.get(target,p,receiver)},set(target, p, newValue, receiver) {const old = Reflect.get(target,p,receiver)if (old===newValue){return}//更新Reflect.set(target,p,newValue,receiver)//通知console.log('update view',p)},defineProperty(target, property, attributes) {//监控删除}})
}const obj={name:1,object:{test:1,}
}
function testVue2() {observe(obj,true)obj.object.test=2
}function testVue3() {const objProxy = observeVue3(obj,true)objProxy.name=2objProxy.object.test=2
}testVue3()

testVue3效果

image-20231011231534420

Compile 解析

class Compile{/**** @param el 根元素* @param vm vue instance*/constructor(el,vm) {this.$vm=vm}/*** 更新界面数据* @param el*/compile(el=new HTMLElement){//使用伪代码写哈const childNodes = el.childNodesfor (const node of childNodes) {//1. vue 通过 nodeType 判断节点类型 具体可以看看此文章 https://www.cnblogs.com/wyongz/p/11446477.html//2. 根据不同类型分开渲染的(例如html元素或纯文本节点)。//3. 无论经过怎样解析需要调用this.update()}}// 通用update方法update(node, exp, dir) {// 获取更新函数 this[dir + 'Updator']是vue源码操作let updator = this[dir + 'Updator'];// 初始化,首次页面赋值//...//...// 创建Watcher  obj   key    functionnew Watcher(this.$vm, exp, function(value) {updator && updator(node, value);})}}class Watcher{constructor($vm, key, updateFunc) {this.$vm=$vmthis.key=keythis.updateFunc=updateFunc//实现将key添加到对相应Dep中 可以使用多种办法这里使用Getter实现Reflect.set(Dep,'watcher',this)$vm[key]Reflect.set(Dep,'watcher',null)}update(){this.updateFunc.call(this.$vm,this.$vm[this.key])}
}
class Dep{watchers=[]constructor(name) {this.depName=name}add(w){this.watchers.push(w)}notify(){this.watchers.forEach(e=>{e.update()})}
}

也需要修改 obeseve,这里以vue2 举例

function defineObserve(obj,key,val) {const dep=new Dep(key)Object.defineProperty(obj,key,{get() {//可以在这里实现收集订阅者,因为Compile需要获取数据,必然需要调用Getterif (Dep['watcher']){dep.add(Dep['watcher'])}return val},set(v) {//新旧相同 passif (val === v){return}//可以在这里调用Dep中所有Watcher的update通知所有订阅界面更新console.log('update view')dep.notify()//更新数据val = v}})
}

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

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

相关文章

conda: error: argument COMMAND: invalid choice: ‘activate‘

参考:https://github.com/conda/conda/issues/13022 输入后重启terminal即可

fisco Java-sdk 快速入门案例

1.安装环境(Ubantu) Linux IDEA下载: https://blog.csdn.net/JOJO_jiongjiong/article/details/123087307 Linux Maven下载: https://zhuanlan.zhihu.com/p/443389963 最好在setting.xml 把maven本地仓库也改一下。 Linux java( 8-14都可以)下载:https://blog.csdn.net/bao…

2023年9月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

9月是加密市场的活动月,斯坦福区块链周、Token2049等大型活动相继举办,后者更是创下超过1万人的历史最高纪录,成为了全球最大的Web3活动。在本次Token2049上,RWA、支付以及出入金成为了讨论度最多的活动。尽管活动如火如荼&#x…

新的“HTTP/2 Rapid Reset”0day攻击打破了DDoS记录

导语 最近,一种名为“HTTP/2 Rapid Reset”的DDoS(分布式拒绝服务)攻击技术成为了热门话题,该技术自8月份以来被积极利用作为零日漏洞,打破了以往的攻击记录。亚马逊网络服务(Amazon Web Services&#xff…

如何调整 Kubernetes StatefulSet 卷的大小

Kubernetes StatefulSet用于在集群内部署有状态应用程序。StatefulSet 中的每个 Pod 都可以访问即使在重新调度后仍坚持使用的本地持久卷。这使得 Pod 能够维护与其集合中的邻居不同的单独状态。 不幸的是,这些卷有一个很大的限制:Kubernetes 没有提供从 StatefulSet 对象调整…

Netty深入浅出Java网络编程学习笔记(三) 优化篇

目录 五、优化 1、拓展序列化算法 序列化接口 枚举实现类 修改原编解码器 2、参数调优 CONNECT_TIMEOUT_MILLIS 使用 源码分析 SO_BACKLOG 三次握手与连接队列 作用 默认值 TCP_NODELAY SO_SNDBUF & SO_RCVBUF ALLOCATOR 使用 ByteBufAllocator类型 RCVBUF_ALLOCATOR 3、RP…

【UE5 Cesium】17-Cesium for Unreal 建立飞行跟踪器(2)

目录 效果 步骤 一、飞机沿航线飞行 二、通过切换相机实现在不同角度观察飞机飞行 效果 步骤 一、飞机沿航线飞行 先去模型网站下载一个波音737飞机模型 然后将下载好的模型导入到UE项目中,导入时需要勾选“合并网格体”(导入前最好在建模软件中将…

Java架构师系统架构设计性能评估

目录 1 导论2 架构评估基础系统性能衡量的基本指标2.1 系统性能的指标2.2 数据库指标2.3 并发用户数2.4 网络延迟2.4 系统吞吐量2.5 资源性能指标3 架构评估基础服务端性能测试3.1基准测试3.2 负载测试3.3 压力测试3.4 疲劳强度测试3.5 容量测试1 导论 本章的主要内容是掌握架构…

机器学习网络模型绘图模板

一 前言 本期为读者推荐一款名为ML Visuals的机器学习画图PPT模板,ML Visuals 专为解决神经网络画图问题设计,通过提供免费的专业的、科学的和充分的视觉和图形来帮助机器学习社区改善科学传播。目前,ML Visuals 包含了超过100多个的自定义图…

Pytorch之SwinTransformer图像分类

文章目录 前言一、Swin Transformer1.Swin Transformer概览2.Patch Partition3.Patch Merging4.W-MSA5.SW-MSA(滑动窗口多头注意力机制)6.Relative Position bias(相对位置偏移)7.网络结构🥇Swin Transformer Block🥈Architecture 二、网络实现1.构建Eff…

opencv图像卷积操作原理,opencv中常用的图像滤波函数

文章目录 opencv图像卷积操作原理,opencv中常用的图像滤波函数一、图像卷积操作原理:1、卷积操作原理图: 二、opencv常用的图像滤波函数:这些函数的主要作用是对图像进行平滑处理或去除噪声(核心目的是减少图像中的噪声&#xff0…

ChatGPT私有数据结合有什么效果?它难吗?

ChatGPT的出现可谓是惊艳了全世界,ChatGPT的问答能力通过了图灵测试,使其回答问题的方式与人类几乎无法区分。大家不甘于只在官方的对话页面问答,想利用 GPT 模型的自然语言能力结合私有数据开拓更多的应用场景。 | ChatGPT私有数据结合特点 …