目录

一、什么是数据劫持?

二、核心 API:Object.defineProperty

三、Vue2 中的数据劫持实现

1. 对象属性的劫持

2. 嵌套对象的处理

3. 数组的特殊处理

四、结合依赖收集的完整流程

五、数据劫持的局限性

六、Vue3 的改进方案

总结


一、什么是数据劫持?

数据劫持指的是拦截对象属性的访问和修改操作的能力。Vue2 通过 JavaScript 的 Object.defineProperty API 实现这一机制,在属性被读取或修改时执行自定义逻辑。

二、核心 API:Object.defineProperty

Object.defineProperty 允许我们精确控制对象属性的行为:

let obj = { name: 'Vue' };
let value = obj.name;Object.defineProperty(obj, 'name', {get() {console.log('读取 name 属性');return value;},set(newVal) {console.log('设置 name 属性', newVal);value = newVal;}
});obj.name; // 控制台输出: "读取 name 属性"
obj.name = 'React'; // 控制台输出: "设置 name 属性 React"

 

三、Vue2 中的数据劫持实现

1. 对象属性的劫持

Vue 在初始化时会遍历 data 中的所有属性:

function defineReactive(obj, key) {let value = obj[key];Object.defineProperty(obj, key, {get() {console.log(`读取 ${key}`);return value;},set(newVal) {console.log(`更新 ${key}`, newVal);value = newVal;// 这里会触发视图更新}});
}const data = { message: 'Hello Vue' };
defineReactive(data, 'message');

 

2. 嵌套对象的处理

Vue 递归劫持嵌套对象:

function observe(data) {if (typeof data !== 'object' || data === null) return;new Observer(data);
}class Observer {constructor(value) {this.value = value;this.walk();}walk() {Object.keys(this.value).forEach(key => {defineReactive(this.value, key);});}
}function defineReactive(obj, key) {let value = obj[key];observe(value); // 递归劫持子属性Object.defineProperty(obj, key, {// getter/setter 略});
}
3. 数组的特殊处理

由于 Object.defineProperty 无法检测数组索引变化,Vue 重写了数组方法:

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {const original = arrayProto[method];arrayMethods[method] = function(...args) {console.log(`数组执行 ${method} 操作`);const result = original.apply(this, args);// 获取新增的元素let inserted;switch(method) {case 'push':case 'unshift':inserted = args;break;case 'splice':inserted = args.slice(2);break;}// 劫持新增元素if (inserted) observeArray(inserted);// 触发视图更新return result;};
});function observeArray(items) {for (let item of items) {observe(item);}
}

 

四、结合依赖收集的完整流程

  1. 初始化阶段

    • 遍历 data 属性,通过 defineProperty 设置 getter/setter

    • 递归处理嵌套对象和数组

  2. 依赖收集

    get() {if (Dep.target) { // 当前正在计算的 Watcherdep.depend();   // 将 Watcher 添加到依赖列表}return value;
    }

    3.派发更新

    set(newVal) {value = newVal;dep.notify(); // 通知所有 Watcher 更新
    }

五、数据劫持的局限性

  1. 无法检测属性添加/删除

    this.obj.newProperty = 'value' // 非响应式

    2. 数组索引和长度修改

this.arr[0] = 'new' // 无法检测
this.arr.length = 10 // 无法检测

   3.解决方案

this.$set(this.obj, 'newProperty', 'value')
this.$delete(this.obj, 'oldProperty')

六、Vue3 的改进方案

Vue3 使用 Proxy 替代 Object.defineProperty

  • 可直接监听整个对象而非属性

  • 支持动态添加属性

  • 完美监听数组变化

  • 性能更优

    const proxy = new Proxy(obj, {get(target, key) {// 拦截读取操作},set(target, key, value) {// 拦截设置操作}
    });

总结

Vue2 的数据劫持机制通过 Object.defineProperty 实现,结合依赖收集派发更新,构建了响应式系统的核心。虽然存在一些局限性,但理解其原理有助于我们:

  1. 更好地使用 Vue 的响应式功能

  2. 避免常见的响应式陷阱

  3. 深入理解 Vue 的设计思想

掌握这些原理,能让你在 Vue 开发中更加得心应手,写出更高效、可维护的代码! 

 

 

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

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

相关文章

数据湖 vs 数据仓库:数据界的“自来水厂”与“瓶装水厂”?

数据湖 vs 数据仓库:数据界的“自来水厂”与“瓶装水厂”? 说起“数据湖”和“数据仓库”,很多刚入行的朋友都会觉得: “听起来好高大上啊!但到底有啥区别啊?是湖更大还是仓库更高端?” 我得说…

Node.js-path模块

Path 模块 path 模块提供了 操作路径 的功能,我们将介绍如下几个较为常用的几个 API ​​path.resolve([…paths]) 将路径片段​​解析为绝对路径​​(从右向左拼接,遇到绝对路径停止) // 若参数为空,返回当前工作目…

Java面试题029:一文深入了解MySQL(1)

欢迎大家关注我的专栏,该专栏会持续更新,从原理角度覆盖Java知识体系的方方面面。 一文吃透JAVA知识体系(面试题)https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=7521898&

vue3.0所采用得Composition Api与Vue2.XOtions Api有什么不同?

Vue 3.0 引入的 Composition API 相较于 Vue 2.x 的 Options API 有显著的不同。下面从几个方面对比这两者的差异: ✅ 1. 代码组织方式不同 Vue 2.x — Options API 使用 data、methods、computed、watch 等分散的选项组织逻辑。 每个功能点分散在不同的选项中&am…

【IP 潮玩行业深度研究与学习】

潮玩行业发展趋势分析:全球市场格局与中国政策支持体系 潮玩产业正经历从"小众收藏"到"大众情绪消费"的深刻转型,2025年中国潮玩市场规模已达727亿元,预计2026年将突破1100亿元,年复合增长率高达26%。这一千…

进程通信-消息队列

消息队列允许一个进程将一个消息发送到一个队列中,另一个进程从该队列中接收这个消息。 使用流程: 写端: 使用结构体 mq_attr 设置消息队列属性,有四个选项: long mq_flags; // 队列属性: 0 表示阻塞 long …

串行通信接口USART,printf重定向数据发送,轮询和中断实现串口数据接收

目录 UART通信协议的介绍 实现串口数据发送 CubeMX配置 printf重定向代码编写 实现串口数据接收 轮询方式实现串口数据接收 接收单个字符 接收不定长字符串(接收的字符串以\n结尾) 中断方式实现串口数据接收 CubeMX配置 UART中断方式接收数据…

Kafka 生产者和消费者高级用法

Kafka 生产者和消费者高级用法 1 生产者的事务支持 Kafka 从版本0.11开始引入了事务支持,使得生产者可以实现原子操作,确保消息的可靠性。 // 示例代码:使用 Kafka 事务 producer.initTransactions(); try {producer.beginTransaction();pr…

k8s中crictl命令常报错解决方法

解决使用crictl命令时报默认端点弃用的报错 报错核心原因 默认端点弃用: crictl 会默认尝试多个容器运行时端点(如 dockershim.sock、containerd.sock 等),但这种 “自动探测” 方式已被 Kubernetes 弃用(官方要求手动…

回转体水下航行器简单运动控制的奥秘:PID 控制和水动力方程的运用

在水下航行器的控制领域中,回转体水下航行器的运动控制是一个关键课题。 今天,就来深入探讨一下其简单运动控制中,PID 控制以及水动力方程的相关运用。 PID 控制的基本原理PID 控制(比例 - 积分 - 微分控制)是一种广…

从入门到精通:npm、npx、nvm 包管理工具详解及常用命令

目录 1. 引言2. npm (Node Package Manager)2.1 定义与用途2.2 常见命令2.3 使用示例 3. npx (Node Package Execute)3.1 定义与用途3.2 常见命令3.3 使用示例3.4 npm 与 npx 的区别 4. nvm (Node Version Manager)4.1 定义与用途4.2 安装 nvm4.3 常见命令4.4 使用示例 5. 工具…

es6特性-第二部分

Promise 介绍和基本使用 Promise是ES6引入的异步编程的新解决方案,主要用来解决回调地狱问题。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。 Promise构造函数:new Promise() Promise.prototype.then方法 Promise.prototype.ca…

java:如何用 JDBC 连接 TDSQL 数据库

要使用JDBC连接TDSQL数据库&#xff08;腾讯云分布式数据库&#xff0c;兼容MySQL协议&#xff09;&#xff0c;请按照以下步骤编写Java程序&#xff1a; 1. 添加MySQL JDBC驱动依赖 在项目的pom.xml中添加依赖&#xff08;Maven项目&#xff09;&#xff1a; <dependenc…

2025年四川省高考志愿填报深度分析与专业导向策略报告——基于599分/24000位次考生-AI

2025年四川省高考志愿填报深度分析与专业导向策略报告——基于599分/24000位次考生 摘要 本报告旨在为预估高考成绩599分、全省物理类位次在24,000名左右的2025年四川考生&#xff0c;提供一份兼具科学性、前瞻性与专业深度的志愿填报策略方案。报告严格遵循“位次法”为核心…

spring boot项目整合百度翻译

本片文章教大家怎样在spring boot项目中引入百度翻译&#xff0c;并且优雅的使用百度翻译。 首先&#xff0c;我们要了解为什么要使用翻译插件。为了支持多语言的国际化&#xff1b; 目前市面上最常见的后端国际化就是在resource资源目录下设置多个语言文档&#xff0c;这些文…

凌晨2点自动备份mysql 数据库,mysql_backup.sh

1、编写备份脚本&#xff1a;vim mysql_backup.sh #!/bin/bash DATE$(date %Y%m%d_%H%M%S) BACKUP_DIR"/data/mysql/backup" USER"backup_user" PASSWORD"backup**"# 逻辑备份所有数据库 mysqldump -u$USER -p$PASSWORD eblp | gzip > $BA…

Linux系统之Tomcat服务

目录 一、Tomcat概述 1、Tomcat介绍 2、Tomcat历史 二、Tomcat原理分析 1、Http工作原理 2、Tomcat整体架构 3、Coyote连接器架构 4、Catalina容器架构 5、Jasper处理流程 6、JSP编译过程 7、Tomcat启动流程 8、Tomcat请求处理流程 三、Tomcat安装与配置 1、单实…

FPGA芯片的供电

FPGA芯片的供电 文章目录 FPGA芯片的供电1. 外部端口供电机制2. 内部逻辑供电机制3. 专有电路供电机制4. 电源稳定性讨论总结 1. 外部端口供电机制 FPGA是专门用于数字系统设计的芯片&#xff0c;能够正确、可靠、高效地和外界其他数字电路进行通信是FPGA芯片必备的一个功能。…

构建可无限扩展的系统:基于 FreeMarker + 存储过程 + Spring Boot 的元数据驱动架构设计

在构建面向多行业、多客户的大型业务系统时&#xff0c;系统的灵活性与扩展能力成为架构设计的核心目标。传统硬编码的开发方式在面对高频变化、复杂组合查询、多租户自定义字段时&#xff0c;往往难以适应。 为了解决上述问题&#xff0c;我们提出一种 以 FreeMarker 脚本托管…

2-深度学习挖短线股-3-训练数据计算

2-3 合并输入特征 首先定义了数据预处理函数&#xff0c;将连续 n 天的 K 线数据&#xff08;如开盘价、收盘价、成交量等&#xff09;合并为一行特征&#xff0c;同时保留对应的目标标签&#xff08;buy 列&#xff0c;表示是否应该买入&#xff09;&#xff1b;然后读取股票代…