vue2响应式数据原理

1. 核心原理

Vue 2 的响应式系统基于 Object.defineProperty,通过 依赖收集派发更新 来实现数据的响应式

  • 依赖收集:在读取数据时,记录哪些函数(或组件)依赖了该数据。
  • 派发更新:在修改数据时,通知所有依赖该数据的函数(或组件)进行更新。

简单示例

// 模块内的变量
let activeFunc = nullfunction observe(obj) {for (const key in obj) {// 临时属性let internalValue = obj[key];// 事件集合let fns = new Set()Object.defineProperty(obj, key, {get: function() {// 依赖收集if(activeFunc){fns.add(activeFunc)}return internalValue;}set: function (val) {internalValue = val;// 如果新值是对象,递归调用 observeif (typeof val === 'object' && val !== null) {observe(val);}//派发更新fns.forEach(fn => fn());}});}
}// 定义vue内部一个模块变量
// 在调用“使用到响应式变量的dom”更新函数的时候,先将该函数赋值给该变量
// 这样vue在依赖收集的时候,就可以通过收集该变量,进而统一收集
function fn1 () {}
function fn2 () {}activeFunc = fn1 // fn表示更新vue模版的函数
fn1()activeFunc = fn2
fn2()
2. 优化后的完整示例
  1. 数组的响应式处理
    • Object.defineProperty 无法直接监听数组的变化(如 pushpop 等操作)。
    • Vue 2 通过重写数组的原型方法来实现数组的响应式。
  2. 依赖收集的封装
    • 将依赖收集和派发更新的逻辑封装成一个独立的 Dep 类。
  3. activeFunc 的管理
    • 使用栈结构管理多个嵌套的依赖关系。
// 将依赖收集和派发更新的逻辑封装成一个独立的类
class Dep {constructor() {this.subscribers = new Set();}depend() {if (activeFunc) {this.subscribers.add(activeFunc);}}notify() {this.subscribers.forEach(fn => fn());}
}// 重写数组的原型方法
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);// Object.defineProperty 无法直接监听数组的变化(如 push、pop 等操作)
// Vue 2 通过重写数组的原型方法来实现数组的响应式
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {const original = arrayProto[method];arrayMethods[method] = function (...args) {const result = original.apply(this, args);dep.notify(); // 派发更新return result;};
});// 当前正在执行的函数
let activeFunc = null;
// 用于管理嵌套的依赖关系
const activeFuncStack = [];// 将当前函数推入栈
function pushActiveFunc(fn) {activeFuncStack.push(fn);activeFunc = fn;
}// 将当前函数弹出栈
function popActiveFunc() {activeFuncStack.pop();activeFunc = activeFuncStack[activeFuncStack.length - 1];
}// 监听对象,将其属性转换为响应式
function observe(obj) {if (Array.isArray(obj)) {// 如果是数组,重写其原型方法obj.__proto__ = arrayMethods;// 递归监听数组元素obj.forEach(item => observe(item));} else if (typeof obj === 'object' && obj !== null) {for (const key in obj) {let internalValue = obj[key];const dep = new Dep();// 递归监听嵌套对象observe(internalValue);Object.defineProperty(obj, key, {get: function () {dep.depend(); // 依赖收集return internalValue;},set: function (val) {internalValue = val;observe(val); // 递归监听新值dep.notify(); // 派发更新}});}}
}// 副作用函数,用于追踪依赖
function watchEffect(fn) {pushActiveFunc(fn);fn(); // 执行函数,触发依赖收集popActiveFunc();
}// 测试
const data = { count: 0, list: [1, 2, 3] };
observe(data);watchEffect(() => {console.log('Count:', data.count);
});watchEffect(() => {console.log('List:', data.list);
});data.count++; // 输出: Count: 1
data.list.push(4); // 输出: List: [1, 2, 3, 4]
3. 关键点解析
  1. 依赖收集
    • get 方法中,通过 dep.depend() 收集当前正在执行的函数(activeFunc)。
    • activeFunc 是通过 watchEffect 设置的,表示当前正在执行的副作用函数。
  2. 派发更新
    • set 方法中,通过 dep.notify() 通知所有依赖该属性的函数进行更新。
  3. 数组的响应式处理
    • 通过重写数组的原型方法,拦截数组的修改操作,并手动触发更新。
  4. 嵌套对象和数组的处理
    • 使用递归监听嵌套对象和数组,确保所有层级的属性都是响应式的。
4. 总结

Vue 2 的响应式系统通过 Object.defineProperty 实现数据的监听,虽然功能强大,但也存在一些局限性(如无法直接监听数组的变化)。通过重写数组方法和封装依赖收集逻辑,Vue 2 实现了完整的响应式系统。

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

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

相关文章

算法日常刷题笔记(2)

为保持刷题的习惯 计划一天刷3-5题 然后一周总计汇总一下 这是第二篇笔记 笔记时间为2月17日到2月23日 第一天 找到初始输入字符串 找到初始输入字符串 Ihttps://leetcode.cn/problems/find-the-original-typed-string-i/ Alice 正在她的电脑上输入一个字符串。但是她打字技…

[实现Rpc] 客户端 | Requestor | RpcCaller的设计实现

目录 Requestor类的实现 框架 完善 onResponse处理回复 完整代码 RpcCaller类的实现 1. 同步调用 call 2. 异步调用 call 3. 回调调用 call Requestor类的实现 (1)主要功能: 客户端发送请求的功能,进行请求描述对服务器…

WPS计算机二级•文档的页面设置与打印

听说这是目录哦 纸张大小页边距和装订线❤️‍🔥打印界面讲解❤️缩印💕打印作文稿纸💞将文档打印成书籍💓限制编辑设置💗给文字文档加密💖文档导出为 PDF格式💘协作编辑模式💝能量站…

hackmyvm-buster

题目地址 信息收集 主机发现 ┌──(root㉿kali)-[/home/kali] └─# arp-scan -I eth1 192.168.56.0/24 Interface: eth1, type: EN10MB, MAC: 00:0c:29:34:da:f5, IPv4: 192.168.56.103 WARNING: Cannot open MAC/Vendor file ieee-oui.txt: Permission denied WARNING: C…

【入门音视频】音视频基础知识

🌈前言🌈 这个系列在我学习过程中,对音视频知识归纳总结的笔记。因为音视频相关讲解非常稀少,所以我希望通过这个音视频系列,跟大家一起学习音视频,希望减少初学者在学习上的压力。同时希望也欢迎指出文章的…

将Ubuntu操作系统的安装源设置为阿里云

在使用Ubuntu操作系统时,默认的软件源通常是国外的仓库,这可能会导致软件安装和更新速度较慢。为了提高下载速度和稳定性,我们可以将Ubuntu的安装源设置为阿里云镜像源。以下是详细步骤: 一、准备工作 在开始之前,请确保您的Ubuntu系统可以正常上网,并且您拥有管理员权…

基于 Python 的项目管理系统开发

基于 Python 的项目管理系统开发 一、引言 在当今快节奏的工作环境中,有效的项目管理对于项目的成功至关重要。借助信息技术手段开发项目管理系统,能够显著提升项目管理的效率和质量。Python 作为一种功能强大、易于学习且具有丰富库支持的编程语言&…

LabVIEW C编译支持工具库CCompileSupp.llb

路径:C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform\CCompileSupp.llb ​ 1. 工具库概述 定位:LabVIEW内置的C语言编译支持工具库,用于处理LabVIEW与C/C代码的混合编程接口,涵盖编译器配置、代码生成…

JVM之JVM的组成

Java 虚拟机(JVM)是 Java 程序的运行核心,它主要由类加载系统、运行时数据区、执行引擎和本地方法接口这几个关键部分组成。 类加载系统(Class Loading System) 类加载系统负责在程序运行时动态地将 Java 类加载到 J…

pycharm 调试 debug 进入 remote_sources

解决办法1: pycharm函数跳转到remote_sources中的文件中_pycharm修改remotesource包存放地址-CSDN博客 file->settings->project structure将项目文件夹设为"Sources"(此时文件夹会变为蓝色)。 解决方法2 Debug:使用Pychar…

iOS App的启动与优化

App的启动流程 App启动分为冷启动和热启动 冷启动:从0开始启动App热启动:App已经在内存中,但是后台还挂着,再次点击图标启动App。 一般对App启动的优化都是针对冷启动。 App冷启动可分为三个阶段: dyld&#xff1a…

StarRocks FE leader节点CPU使用率周期性的忽高忽低问题分析

背景 本文基于 StarRocks 3.3.5 最近在做一些 StarRocks 相关的指标监控的时候,看到了FE master的CPU使用率相对其他FE节点是比较高的,且 呈现周期性的变化(周期为8分钟), 于此同时FE master节点的GC频率相对于其他节…

Spring高级篇-Spring IOC容器 Aware 接口

一、概述 在Spring框架中,IOC(Inversion of Control)容器负责管理应用程序中的对象(即Bean)的生命周期和依赖关系。Spring提供了一系列的Aware接口,允许Bean在初始化时获取Spring容器中的某些资源或信息。…

数字信任的底层逻辑:密码学核心技术与现实应用

安全和密码学 --The Missing Semester of Your CS Education 目录 熵与密码强度密码散列函数密钥体系 3.1 对称加密 3.2 非对称加密信任模型对比典型应用案例安全实践建议扩展练习杂项 密码学是构建数字信任的基石。 本文浅析密码学在现实工具中的应用,涵盖 1&…

MySQL数据库连接池泄露导致MySQL Server超时关闭连接

前言 最近做项目,发现老项目出现xxx,这个错误其实很简单,出现在MySQL数据库Server端对长时间没有使用的client连接执行清楚处理,因为是druid数据库,且在github也出现这样的issue:The last packet successf…

DirectX12(D3D12)基础教程三 线性代数与3D世界空间

线性代数是数学的一个分支,它的研究对象是向量,向量空间(或称线性空间),线性变换和有限维的线性方程组。 向量和矩阵是学习3D入门最基本的理论基础。本章重点讲向量和矩阵. 向量概念 向量最基本的定义就是一个方向和…

LeetCode 230.二叉搜索树中第K小的元素

题目:给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 思路: 代码: /*** Definition for a binary tree node.* public class Tre…

Android 老项目 jcenter 库失效

最近重新维护了一些老项目发现大部分jcenter库失效了, Could not resolve com.xx:2.1.3. 如果你也遇到了,不妨试试 替换为 aliyun的jcenter服务,就不用一个个找代替库了。 project 下的 build.gradle 文件添加: maven { url htt…

Python数据结构:哈希表-高效存储与查找的秘密武器!

大家周一好!今天我们来聊聊Python中一个非常重要的数据结构——哈希表。无论是算法面试还是实际开发,哈希表都扮演着至关重要的角色。掌握它,你就能轻松解决许多复杂的编程问题! 在编程中,如何实现快速的存储与查找操…

【复习】Redis

数据结构 Redis常见的数据结构 String&#xff1a;缓存对象Hash&#xff1a;缓存对象、购物车List&#xff1a;消息队列Set&#xff1a;点赞、共同关注ZSet&#xff1a;排序 Zset底层&#xff1f; Zset底层的数据结构是由压缩链表或跳表实现的 如果有序集合的元素 < 12…