vue2双向绑定解析

Vue 2 的双向绑定原理基于 Object.defineProperty,核心源码在 src/core/observer 目录中。

1. 核心模块:observer

observer 模块负责将普通对象转换为响应式对象,主要包括以下文件:

  • index.js:定义 Observer 类,用于将对象转换为响应式。
  • dep.js:定义 Dep 类,用于管理依赖(订阅者)。
  • watcher.js:定义 Watcher 类,用于监听数据变化并触发更新。

2. Observer 类

Observer 类是 Vue 2 响应式系统的核心,它通过 Object.defineProperty 将对象的属性转换为 getter 和 setter,从而实现依赖收集和派发更新。

源码位置:src/core/observer/index.js

export class Observer {value: any;dep: Dep;vmCount: number;constructor(value: any) {this.value = value;this.dep = new Dep();this.vmCount = 0;def(value, '__ob__', this); // 将 Observer 实例挂载到对象的 __ob__ 属性上if (Array.isArray(value)) {// 处理数组if (hasProto) {protoAugment(value, arrayMethods);} else {copyAugment(value, arrayMethods, arrayKeys);}this.observeArray(value);} else {// 处理对象this.walk(value);}}walk(obj: Object) {const keys = Object.keys(obj);for (let i = 0; i < keys.length; i++) {defineReactive(obj, keys[i]);}}observeArray(items: Array<any>) {for (let i = 0, l = items.length; i < l; i++) {observe(items[i]);}}
}
  • Observer 类会递归地将对象的属性转换为响应式。
  • 对于数组,Vue 2 通过重写数组的变异方法(如 pushpop 等)来实现响应式。

3. defineReactive 函数

defineReactive 是 Vue 2 实现响应式的核心函数,它通过 Object.defineProperty 定义属性的 getter 和 setter

源码位置:src/core/observer/index.js

export function defineReactive(obj: Object,key: string,val: any,customSetter?: ?Function,shallow?: boolean
) {const dep = new Dep(); // 每个属性都有一个 Dep 实例,用于管理依赖const getter = property && property.get;const setter = property && property.set;if ((!getter || setter) && arguments.length === 2) {val = obj[key];}let childOb = !shallow && observe(val); // 递归处理嵌套对象Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {const value = getter ? getter.call(obj) : val;if (Dep.target) {dep.depend(); // 收集依赖if (childOb) {childOb.dep.depend();if (Array.isArray(value)) {dependArray(value);}}}return value;},set: function reactiveSetter(newVal) {const value = getter ? getter.call(obj) : val;if (newVal === value || (newVal !== newVal && value !== value)) {return;}if (setter) {setter.call(obj, newVal);} else {val = newVal;}childOb = !shallow && observe(newVal); // 对新值进行响应式处理dep.notify(); // 通知依赖更新},});
}
  • getter:在访问属性时,调用 dep.depend() 收集依赖。
  • setter:在修改属性时,调用 dep.notify() 通知依赖更新。

4. Dep 类

Dep 类是依赖管理器,用于存储和管理 Watcher 实例。

源码位置:src/core/observer/dep.js

export default class Dep {static target: ?Watcher;id: number;subs: Array<Watcher>;constructor() {this.id = uid++;this.subs = [];}addSub(sub: Watcher) {this.subs.push(sub);}removeSub(sub: Watcher) {remove(this.subs, sub);}depend() {if (Dep.target) {Dep.target.addDep(this);}}notify() {const subs = this.subs.slice();for (let i = 0, l = subs.length; i < l; i++) {subs[i].update();}}
}
  • Dep.target:当前正在计算的 Watcher 实例。
  • subs:存储所有订阅了该属性的 Watcher 实例。
  • notify:通知所有订阅者更新。

5. Watcher 类

Watcher 类是 Vue 2 中用于监听数据变化的订阅者,它会在数据变化时触发回调函数。

源码位置:src/core/observer/watcher.js

export default class Watcher {vm: Component;expression: string;cb: Function;id: number;deps: Array<Dep>;newDeps: Array<Dep>;depIds: SimpleSet;newDepIds: SimpleSet;getter: Function;value: any;constructor(vm: Component,expOrFn: string | Function,cb: Function,options?: ?Object,isRenderWatcher?: boolean) {this.vm = vm;this.cb = cb;this.id = ++uid; // uid for batchingthis.deps = [];this.newDeps = [];this.depIds = new Set();this.newDepIds = new Set();this.getter = parsePath(expOrFn);this.value = this.get();}get() {pushTarget(this); // 将当前 Watcher 设置为 Dep.targetlet value;const vm = this.vm;try {value = this.getter.call(vm, vm);} catch (e) {// 处理错误} finally {popTarget(); // 恢复之前的 Watcherthis.cleanupDeps();}return value;}update() {queueWatcher(this); // 将 Watcher 加入队列,等待批量更新}run() {const value = this.get();if (value !== this.value || isObject(value)) {const oldValue = this.value;this.value = value;this.cb.call(this.vm, value, oldValue); // 执行回调}}
}
  • Watcher 实例会在初始化时调用 get 方法,触发属性的 getter,从而收集依赖。
  • 当数据变化时,Watcher 的 update 方法会被调用,最终触发回调函数。

Vue 2 的双向绑定原理基于 Object.defineProperty,通过以下步骤实现:

  1. 响应式化Observer 类将对象的属性转换为 getter 和 setter
  2. 依赖收集:在 getter 中调用 dep.depend(),将当前 Watcher 添加到依赖列表中。
  3. 派发更新:在 setter 中调用 dep.notify(),通知所有依赖的 Watcher 更新。
  4. 批量更新Watcher 的更新会被加入队列,异步执行,以提高性能。

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

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

相关文章

中级软件设计师2004-2024软考真题合集下载

中级软件设计师2004-2024软考真题合集下载 &#x1f31f; 资源亮点&#x1f3af; 适用人群&#x1f4a1; 资源使用指南&#x1f4cc; 资源获取方式 &#x1f31f; 资源亮点 「中级软件设计师历年真题及答案解析&#xff08;2004-2024&#xff09;」 是全网最全、最新的备考资料…

艾尔登复刻Ep1——客户端制作、场景切换、网络控制

需要添加的插件内容 Netcode for GameObjects&#xff1a;是一个为 Unity 游戏开发提供高级网络功能的 SDK。它的主要作用是允许开发者在其 GameObject 和 MonoBehaviour 工作流中集成网络功能&#xff0c;并且可以与多种底层传输层协议兼容。 具体内容请看&#xff1a;https:…

2025探索短剧行业新可能报告40+份汇总解读|附PDF下载

原文链接&#xff1a;https://tecdat.cn/?p41043 近年来&#xff0c;短剧以其紧凑的剧情、碎片化的观看体验&#xff0c;迅速吸引了大量用户。百度作为互联网巨头&#xff0c;在短剧领域积极布局。从早期建立行业专属模型冷启动&#xff0c;到如今构建完整的商业生态&#xf…

正常的一个编码器的架构是怎么样,有哪些模块构成

正常的一个编码器架构及其模块构成 在音视频编码器&#xff08;Video Encoder&#xff09;中&#xff0c;通常包括多个核心模块&#xff0c;整个编码器架构遵循 输入 -> 预处理 -> 编码核心 -> 码流封装 的流程。 1. 编码器的整体架构 编码器的主要架构如下&#xf…

文件解析漏洞练习

iis6的目录解析漏洞 (.asp目录中的所有文件都会被当做asp文件执行) 1.在iis的网站根目录新建一个名为x.asp的文件 2.在x.asp中新建一个jpg文件。内容为<%now()%> asp代码。 3.在外部浏览器中访问windows2003的iis网站中的2.jpg 发现asp代码被执行 iis6的分号截断解析漏洞…

SQL Server性能优化实战:从瓶颈定位到高效调优

引言 在数据库应用中,性能问题直接影响用户体验和系统稳定性。本文基于实际案例,分享SQL Server性能优化的关键步骤与实用技巧,涵盖问题定位、索引优化、查询调优等多个维度。 目录 引言 一、性能瓶颈定位 1.1 监控工具使用 二、索引优化实战 2.1 索引碎片整理 2.2 缺…

【DNS系列】使用TCP传输

DNS ​默认使用UDP协议​&#xff08;端口53&#xff09;进行通信&#xff0c;但在以下场景中会切换到TCP协议​&#xff08;端口53&#xff09;&#xff1a; ​1. 响应数据过大&#xff08;超过512字节&#xff09;​ ​UDP限制&#xff1a;DNS的UDP协议默认限制单个数据包大…

Go Ebiten小游戏开发:俄罗斯方块

在这篇文章中&#xff0c;我们将一起开发一个简单的俄罗斯方块游戏&#xff0c;使用Go语言和Ebiten游戏库。Ebiten是一个轻量级的游戏库&#xff0c;适合快速开发2D游戏。我们将逐步构建游戏的基本功能&#xff0c;包括游戏逻辑、图形绘制和用户输入处理。 项目结构 我们的项…

MySQL中IN关键字与EXIST关键字的比较

文章目录 **功能等价性分析****执行计划分析**&#xff1a; **1. EXISTS 的工作原理****步骤拆解**&#xff1a; **2. 为什么需要“利用索引快速定位”&#xff1f;****索引作用示例**&#xff1a; **3. 与 IN 子查询的对比****IN 的工作方式**&#xff1a;**关键差异**&#x…

## DeepSeek写水果记忆配对手机小游戏

DeepSeek写水果记忆配对手机小游戏 提问 根据提的要求&#xff0c;让DeepSeek整理的需求&#xff0c;进行提问&#xff0c;内容如下&#xff1a; 请生成一个包含以下功能的可运行移动端水果记忆配对小游戏H5文件&#xff1a; 要求 可以重新开始游戏 可以暂停游戏 卡片里的水果…

【含文档+PPT+源码】基于Django框架的乡村绿色农产品交易平台的设计与实现

项目介绍 本课程演示的是一款基于Django框架的乡村绿色农产品交易平台的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Python学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运…

idea超级AI插件,让 AI 为 Java 工程师

引言​ 用户可在界面中直接通过输入自然语言的形式描述接口的需求&#xff0c;系统通过输入的需求自动分析关键的功能点有哪些&#xff0c;并对不确定方案的需求提供多种选择&#xff0c;以及对需求上下文进行补充&#xff0c;用户修改确定需求后&#xff0c;系统会根据需求设…

@RestControllerAdvice注解

RestControllerAdvice RestControllerAdvice 是 Spring Framework&#xff08;3.2&#xff09;和 Spring Boot 中用于全局处理控制器层异常和统一响应格式的注解。它结合了 ControllerAdvice 和 ResponseBody 的功能&#xff0c;能够拦截控制器方法抛出的异常&#xff0c;并以 …

ActiveMQ监听器在MQ重启后不再监听问题

应用的监听器注解 JmsListener(destination "TopicName",containerFactory "FactoryName")工厂代码 BeanJmsListenerContainerFactory<?> FactoryName(ConnectionFactory connectionFactory){SimpleJmsListenerContainerFactory factory new S…

大白话 Vue 中的keep - alive组件,它的作用是什么?在什么场景下使用?

大白话 Vue 中的keep - alive组件&#xff0c;它的作用是什么&#xff1f;在什么场景下使用&#xff1f; 什么是 keep-alive 组件 在 Vue 里&#xff0c;keep-alive 是一个内置组件&#xff0c;它就像是一个“保存盒”&#xff0c;能把组件实例保存起来&#xff0c;而不是每次…

考研复试c语言常见问答题汇总2

11. 关键字和一般标识符有什么不同&#xff1f; C语言中关键字与一般标识符区别&#xff1a; 定义&#xff1a;关键字是C语言预定义的特殊单词&#xff08;如int、for&#xff09;&#xff0c;有固定含义&#xff1b;标识符是自定义的名称&#xff08;如变量名、函数名&#xf…

Scala编程_实现Rational的基本操作

在Scala中实现一个简单的有理数&#xff08;Rational&#xff09;类&#xff0c;并对其进行加法、比较等基本操作. 有理数的定义 有理数是可以表示为两个整数的比值的数&#xff0c;通常形式为 n / d&#xff0c;其中 n 是分子&#xff0c;d 是分母。为了确保我们的有理数始终…

若依框架-给sys_user表添加新字段并获取当前登录用户的该字段值

目录 添加字段 修改SysUser类 修改SysUserMapper.xml 修改user.js 前端获取字段值 添加字段 若依框架的sys_user表是没有age字段的&#xff0c;但由于业务需求&#xff0c;我需要新添加一个age字段&#xff1a; 修改SysUser类 添加age字段后&#xff0c;要在SysUser类 …

霍夫变换法是基于传统视觉特征的道路车道线检测算法中的一种经典方法

霍夫变换法是基于传统视觉特征的道路车道线检测算法中的一种经典方法&#xff0c;以下是对它的详细介绍&#xff1a; 基本原理 霍夫变换的基本思想是将图像空间中的点映射到参数空间中&#xff0c;通过在参数空间中寻找峰值来确定图像中特定形状的参数。在车道线检测中&#…

【论文笔记】Best Practices and Lessons Learned on Synthetic Data for Language Models

论文信息 论文标题&#xff1a;Best Practices and Lessons Learned on Synthetic Data for Language Models 作者信息&#xff1a; Ruibo Liu, Jerry Wei, Fangyu Liu, Chenglei Si, Yanzhe Zhang, Jinmeng Rao, Steven Zheng, Daiyi Peng, Diyi Yang, Denny Zhou1 and Andre…