Vue2 响应式原理

Vue 的响应式原理

Vue 的响应式原理基于"数据劫持"和"依赖收集"的概念。当我们将一个普通的 JavaScript 对象传递给 Vue 实例的 data 选项时,Vue 将遍历此对象的所有属性,并使用 Object.defineProperty()来对每个属性进行 getter 和 setter 的重写,数据变化时能够触发视图更新。

数据劫持

Vue 通过使用 Object.defineProperty 方法对数据对象进行数据劫持。它会重写对象的属性访问器(getter和setter),使得当属性被读取或修改时,Vue 能够捕捉到这一操作,并触发相应的更新。

  下面是一个简单的例子,展示了如何将一个普通对象转化为响应式数据对象:

function defineReactive(data, key, value) {Object.defineProperty(data, key, {enumerable: true,configurable: true,get() {console.log(`读取属性 ${key}: ${value}`);return value;},set(newValue) {console.log(`设置属性 ${key}: ${newValue}`);value = newValue;},});
}const obj = {};
defineReactive(obj, 'message', 'Hello, Vue!');
console.log(obj.message);  // 读取属性 message: Hello, Vue!
obj.message = 'Hello, World!';  // 设置属性 message: Hello, World!

在上述代码中,我们定义了 defineReactive 函数,它使用 Object.defineProperty 对属性进行劫持。当属性被读取时,会打印相应的信息,当属性被修改时,也会打印对应的信息。

依赖收集

在 Vue 的响应式系统中,依赖收集是指收集数据属性的依赖关系,也就是说,当一个属性被使用时,Vue 会追踪到这个属性的依赖,并建立起一个关联关系。这样,当依赖的属性发生变化时,Vue 就能够知道哪些地方需要更新。

Vue 通过使用"观察者"和"依赖"的概念来实现依赖收集。每个被劫持的属性都会关联一个"Dep"对象,它负责追踪所有依赖于该属性的"观察者"。当属性被修改时,"Dep"对象会通知所有相关的"观察者"进行更新操作。

  下面是一个简化的例子,展示了如何实现一个简单的观察者和依赖关系:

// dep 是个可观察对象,可以有多个指令订阅它
class Dep {constructor() {this.subscribers = [];}// 将观察对象和 watcher 建立依赖depend() {if (Dep.target && !this.subscribers.includes(Dep.target)) {this.subscribers.push(Dep.target);}}// 发布通知notify() {// 调用每个订阅者的 update 方法实现更新this.subscribers.forEach(subscriber => subscriber.update());}
}
// Dep.target 用来存放目前正在使用的 watcher
// 全局唯一,并且一次也只能有一个 watcher 被使用
Dep.target = null;class Watcher {constructor(updateCallback) {this.updateCallback = updateCallback;}update() {this.updateCallback();}
}function defineReactive(data, key, value) {const dep = new Dep();Object.defineProperty(data, key, {enumerable: true,configurable: true,get() {// 建立依赖dep.depend();return value;},set(newValue) {value = newValue;// 通知订阅者dep.notify();},});
}const obj = {};
defineReactive(obj, 'message', 'Hello, Vue!');function render() {console.log(obj.message);
}Dep.target = new Watcher(render);
obj.message = 'Hello, World!';  
// 输出: Hello, World!

在上述代码中,我们定义了 Dep 类作为依赖追踪的容器,它维护了一个订阅者列表。每个被劫持的属性都会对应一个 Dep 实例,用于收集依赖和通知更新。当属性被读取时,会调用dep.depend()方法来收集依赖,将当前的观察者(Dep.target)添加到订阅者列表中。当属性被修改时,会调用dep.notify()方法来通知所有相关的观察者进行更新。

另外,我们定义了 Watcher 类作为观察者,它接收一个更新回调函数。在 render 函数中,我们创建了一个 Watcher 实例,并将render函数作为更新回调函数传递给它。然后,将 Dep.target 设置为当前的观察者,再修改 obj.message 属性的值。这样,当 obj.message 属性发生变化时,Dep 会通知到相关的观察者,触发相应的更新操作。

Vue的响应式原理处理数组和对象的变化

数组的变化

  当对数组进行变异操作(如push、pop、shift、unshift、splice、sort、reverse等)时,Vue能够捕获到这些变化,并触发视图的更新。

  Vue通过重写数组的变异方法,即对这些方法进行了劫持,来实现对数组的监听和触发更新。这些变异方法有以下特点:

  • 它们会修改原始数组本身,而不是返回一个新的数组
  • 它们被重写成能够触发更新的形式

  举个例子,当我们使用 push 方法向数组中添加元素时,Vue会捕获到这个变化并触发相应的更新:

data: {items: []
}// 将一个新元素添加到数组中
this.items.push('new item');

当调用push方法时,Vue会检测到这个变化,并触发视图的更新,以显示新的数组内容。

需要注意的是,对数组进行以下操作时,Vue无法自动触发更新:

  • 通过索引直接修改数组元素的值:this.items[index] = newValue
  • 修改数组的长度:this.items.length = newLength

  对于上述情况,我们可以使用以下方法来触发更新:

// Vue.set 方法
Vue.set(this.items, index, newValue);// this.$set 方法
this.$set(this.items, index, newValue);// 或者使用 splice 方法
this.items.splice(index, 1, newValue);

通过上述方法,我们可以通知 Vue 进行更新。

对象的变化

  对于对象的变化,Vue 的响应式系统使用了类似的方法进行劫持。

  当我们修改对象的属性值时,Vue 能够捕获到这个变化并触发更新。这是因为 Vue 在对象上使用了 Object.defineProperty 来重写属性的访问器(getter和setter),从而能够追踪对象属性的变化。

data: {user: {name: 'John',age: 25}
}// 修改对象的属性值
this.user.name = 'Jane';

当我们修改 user 对象的 name 属性时,Vue 能够捕获到这个变化并触发相应的更新。

需要注意的是,如果要在响应式对象上添加新的属性,需要使用以下方法:

// 使用Vue.set方法
Vue.set(this.user, 'address', '123 Main St');// 或者使用扩展运算符
this.user = { ...this.user, address: '123 Main St' };

通过上述方法,我们可以让新添加的属性也具有响应性,Vue能够追踪到这个变化并触发更新。

最后
  通过数据劫持和依赖收集,Vue 的响应式系统能够追踪数据的变化并自动更新相应的UI。这使得开发者能够以声明式的方式来处理数据,而不需要手动操作DOM。在实际的Vue应用中,这个响应式原理被广泛应用于数据绑定、计算属性、侦听器等方面,极大地简化了开发流程。

参考:面试常问之Vue响应式原理

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

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

相关文章

Redis ttl与key过期策略

TTL ttl --- time to line 网络原理的IP协议当中,IP协议报头中,就有一个字段,TTL IP中的TTL不是用时间衡量过期的,而是用次数 功能:查看当前的key的过期时间还剩多少 语法: ttl key 时间复杂度&#xf…

12.C++常用的算法_遍历算法

文章目录 遍历算法1. for_each()代码工程运行结果 2. transform()代码工程运行结果 3. find()代码工程运行结果 遍历算法 1. for_each() 有两种方式&#xff1a; 1.普通函数 2.仿函数 代码工程 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vect…

layui复选框勾选取消勾选事件监听

监听事件放置位置&#xff1a; form.on(checkbox(equipInputClick), function(data){var a data.elem.checked;var val data.value;if(a true){}else{}});html部分 <input lay-filter"equipInputClick" type"checkbox" lay-skin"primary&quo…

前端做了快两年了,盘点一下我的前端技术栈

前言 前言很简单&#xff0c;年终了&#xff0c;自己写了快两年前端&#xff08;虽然刚毕业&#xff09;&#xff0c;但是大致总结一下哈哈哈哈 我觉得这个话题蛮有意思的&#xff0c;可以看看大家的技术广度&#xff0c;可以进行分享和学习以及讨论 所以这里说一下我对我的…

# ABAP SQL 字符串处理

经常我都要在ABAP的sql语句中对字符串进行处理&#xff0c;现在就总结一下可以用到的方法 文章目录 字符串处理拼接字段运行结果 填充字符串运行结果 截取字符串 SUBSTRING运行结果 CAST转换类型程序运行结果 字符串处理 在SQL语句中&#xff0c;有时候会有需要拼接字段或者是…

车载平板电脑丨车载终端丨浅聊叉车仓储出入库管理解决方案

众所周知&#xff0c;叉车是制造业自动化升级的核心设备。 十三五期间&#xff0c;提出中国制造 2025 战略&#xff0c;其核心内容就是要用机器代替人工&#xff0c;提升生产效率&#xff0c;降低对劳动力的依赖性。而叉车作为起重、搬运等普通劳动力替代的核心设备&#xff0…

Spring中基于事件监听驱动 和 线程池的异步任务

文章目录 事件监听驱动 与 异步事件源ApplicationContextAware接口 发布事件事件实体监听事件实现异步注入綫程池 事件驱动机制&#xff0c;与MQ消息队列比较 事件监听驱动 与 异步 事件监听驱动优点&#xff1a;解耦&#xff0c;将 事件和业务进行解耦&#xff0c;通过Asyc注解…

React 组件生命周期函数的用法和示例代码

React 中的生命周期函数可以分为三个阶段&#xff1a;Mounting&#xff08;挂载&#xff09;&#xff0c;Updating&#xff08;更新&#xff09;和 Unmounting&#xff08;卸载&#xff09;。每个阶段都有不同的函数&#xff0c;用于执行不同的操作。 Mounting&#xff08;挂载…

C++解决大学课设所有管理系统(增删查改)

C一篇解决大学课设所有**管理系统(增删查改) 文章目录 C一篇解决大学课设所有**管理系统(增删查改)1.引言1.1 使用结果展示 2. 基本原理3. 文件层次结构4.具体实现(通讯录管理系统为例)4.1 通讯录实体类(addressbook.h)4.2 通讯录实现类(addressbook.cpp)4.3 通讯录管理类&…

设备树的概念及引入、设备树如何变成device

在平台总线驱动模型中资源和驱动已经从逻辑上和代码组织上进行了分离&#xff0c;但每次调整资源还是会涉及到内核&#xff0c;所以现在更加流行的是设备树方式。设备树的好处是通过独立于内核存在&#xff0c;这样如果设备上外设功能启用与否以及位置变动的话很多时候不用修改…

【股权激励】“三级股权分配法”——某互联网电商企业股权激励管理

【客户背景及面临难题】K公司于2014年上线&#xff0c;是由多位创始人联合创办的一家以众包微物流配送为核心模式&#xff0c;主营同城水果、蔬菜、生鲜、日常生活用品、零食等商品在线销售&#xff0c;专注于社区生鲜最后一公里配送&#xff0c;主打一小时之内闪电送达的互联网…

Vue自定义指令介绍及使用方法

介绍​ 除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外&#xff0c;Vue 还允许你注册自定义的指令 (Custom Directives)。 之前已经介绍了两种在 Vue 中重用代码的方式&#xff1a;组件 和 组合式函数。组件是主要的构建模块&#xff0c;而组合式函数则侧重于有状态…

使用C语言函数对数组进行操作

前言 在我们了解数组和函数之后&#xff0c;我们对数组和函数进行结合&#xff0c;之后完成一些操作吧 题目描述 杰克想将函数与数组结合进行一些操作&#xff0c;以下是他想要达到的效果&#xff0c;请你帮帮他吧&#xff01; 创建一个整型数组&#xff0c;完成对数组的操作 1…

签约速递 | 看零信任如何打造安全互联的数字底座?

近期&#xff0c;易安联再传佳音&#xff0c;签约南通市公安局、隆基绿能科技股份有限公司、南京体育学院等&#xff0c;持续为客户提供功能更完善、体验更好的零信任安全解决方案&#xff0c;赋能千行百业数字化转型升级&#xff0c;共创网络安全繁荣生态。 南通市公安局 数字…

c语言:getchar()和getch()的区别

1.getchar( ): 使用getchar( )函数读取键盘输入时&#xff0c;只有用户敲击回车键后&#xff0c;getchar( )函数才会返回字符。getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区(输入缓冲区)中.直到用户按回车为止(回车字…

K-means聚类算法的原理、应用与实例

文章目录 K-means 聚类算法&#xff1a;原理K-means 聚类算法的应用K-means 聚类算法的优化与改进 一个使用 K-means 聚类算法进行客户细分的简单实例 K-means 聚类算法&#xff1a;原理 K-means 算法是一种经典的无监督学习方法&#xff0c;用于对未标记的数据集进行分群&…

智慧公厕是智慧城市建设中不可或缺的一部分

智慧城市的数字化转型正在取得显著成效&#xff0c;各项基础设施的建设也在迅速发展&#xff0c;其中智慧公厕成为了智慧城市体系中不可或缺的一部分。作为社会生活中必要的设施&#xff0c;公共厕所的信息化、数字化、智慧化升级转型能够实现全区域公共厕所管理的横向打通和纵…

数据处理-pandas之多sheet专题

pandas读取具有多个sheet的xlsx文件 import pandas as pddf1 pd.read_excel(data_path, sheet_nameNone) df2 pd.read_excel(data_path) print(df1.keys()) print(df2.keys())两个print不一样&#xff0c;如果不设定sheet_name为None&#xff0c;默认指读取一个sheet&#x…

PostgreSQL介绍

PostgreSQL是一个高度先进的对象关系型数据库管理系&#xff08;ORDBMS&#xff09;&#xff0c;其起源可以追溯到1986年&#xff0c;最初是加州大学伯克利分校计算机系的一个项目&#xff0c;名为POSTGRES。它是从Ingres项目演变而来的&#xff0c;目的是克服当时关系数据库系…

macOS 启动 Nacos(2),50家大厂面试万字精华总结

进来之后一定要选择支持 arm64 的版本不然会出现因为版本不支持导致无法启动 WARNING: The requested images platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested点击 copy docker pull nacos/naco…