vue2响应式原理----发布订阅模式

很多人感觉vue2的响应式其实用到了观察者+发布订阅。我们先来看一下简单的发布订阅的代码:

// 调度中心
class Dep {static subscribes = {}// 订阅所有需求static subscribe (key, demand) {// 对需求分类收集if (!Dep.subscribes[key]) Dep.subscribes[key] = []Dep.subscribes[key].push(demand)}// 对所有订阅者发布通知static publish (key, age) {if (!Dep.subscribes[key]) returnfor (const demand of Dep.subscribes[key]) {demand(age)}}}// 找对象的猎手类class Watcher {constructor (name, age) {this.name = name // 名字this.age = age // 年龄}// 订阅,由调度中心将猎手需求分类并存放到全局subscribe (key, demand) {Dep.subscribe(key, demand)}// 发布,由调度中心将同分类下的需求全部触发publish (key, age) {Dep.publish(key, age)}}// 猎手注册const aa = new Watcher('aa', 18)const bb = new Watcher('bb', 20)// 猎手订阅自己感兴趣的人aa.subscribe('key', function (age) {if (age === aa.age) console.log(`我是aa,我们都是${age}`)else console.log(`我是aa,我们年龄不同`)})bb.subscribe('key', function (age) {if (age === bb.age) console.log(`我是bb,我们都是${age}`)else console.log(`我是bb,我们年龄不同`)})// 红娘注册const red = new Watcher('red', 35)// 红娘发布信息red.publish('key', 20)// 我是aa,我们年龄不同// 我是bb,我们都是20

从上面中发现一个重要的点,发布者和订阅者是根据key值来区分的,然后通过消息中心来中转的,他们家是是实现不知道对方是谁。
而观察者模式中观察者是一开始就知道自己观察的是谁。

上面其实就是简易版的vue原理中发布订阅那段,我们接下来看完整过程。

Vue2 的响应式

  1. 创建一个 Observer 对象,它的主要作用是给对象的每个属性添加 getter 和 setter 方法。
  2. 在 getter 和 setter 方法中分别进行依赖的收集和派发更新。
  3. 创建 Watcher 对象,用于监听数据的变化,当数据发生任何变化时,Watcher 对象会触发自身的回调函数。
  4. 在模板解析阶段,对模板中使用到的数据进行依赖的收集,即收集 Watcher 对象。
  5. 当数据发生变化时,Observer 对象会通知 Dep 对象调用 Watcher 对象的回调函数进行更新操作,即派发更新。
  6. 更新完毕后,Vue2 会进行视图的重新渲染,从而实现响应式。

下面是一个基于 Object.defineProperty 实现响应式的示例,仅供参考:

function observe(obj) {if (!obj || typeof obj !== 'object') {return;}Object.keys(obj).forEach(key => {// 尝试递归处理observe(obj[key]);let val = obj[key];const dep = new Dep(); // 新建一个依赖Object.defineProperty(obj, key, {enumerable: true,configurable: true,get() {if (Dep.target) {dep.depend(); // 收集依赖}return val;},set(newVal) {if (newVal === val) {return;}val = newVal;dep.notify(); // 派发更新}});});
}// 依赖类
class Dep {constructor() {this.subs = [];}addSub(sub) {this.subs.push(sub);}removeSub(sub) {const index = this.subs.indexOf(sub);if (index !== -1) {this.subs.splice(index, 1);}}depend() {if (Dep.target) {Dep.target.addDep(this);}}notify() {this.subs.forEach(sub => sub.update());}
}Dep.target = null;// 观察者类
class Watcher {constructor(vm, expOrFn, callback) {this.vm = vm;this.getter = parsePath(expOrFn);this.callback = callback;this.value = this.get(); // 初始化,触发依赖}get() {Dep.target = this; // 设置当前依赖const value = this.getter.call(this.vm, this.vm); // 触发 getterDep.target = null; // 清除当前依赖return value;}addDep(dep) {dep.addSub(this);}update() {const oldValue = this.value;this.value = this.get(); // 重新获取this.callback.call(this.vm, this.value, oldValue); // 触发回调}
}// 解析路径
function parsePath(expOrFn) {if (typeof expOrFn === 'function') {return expOrFn;}const segments = expOrFn.split('.');return function(obj) {for (let i = 0; i < segments.length; i++) {if (!obj) {return;}obj = obj[segments[i]];}return obj;};
}// 测试
const obj = { foo: 'foo', bar: { a: 1 } };
observe(obj);
new Watcher(obj, 'foo', (val, oldVal) => {console.log(`foo changed from ${oldVal} to ${val}`);
});
new Watcher(obj, 'bar.a', (val, oldVal) => {console.log(`bar.a changed from ${oldVal} to ${val}`);
});obj.foo = 'FOO'; // 输出 `foo changed from foo to FOO`
obj.bar.a = 2; // 输出 `bar.a changed from 1 to 2`

以上代码中,函数 observe 用于递归遍历对象属性,把其进行劫持,包括收集依赖和派发更新;类 Dep 代表一个依赖,其中 addSub 用于添加订阅者实例,removeSub 用于移除订阅者实例,depend 用于收集依赖,即把当前依赖加到对应的订阅者中,notify 用于派发更新,即遍历所有订阅者,并触发其回调函数。类 Watcher 则代表一个订阅者,其中 getter 用于获取数据,callback 用于回调函数,addDep 用于添加依赖,即把当前订阅者添加到对应的依赖中,update 用于更新值,并触发相应的回调函数,如有必要。函数 parsePath 则用于解析路径字符串,返回对应属性的值。

例子中我们对对象 obj 进行了劫持,同时创建了两个观察者,分别对应 foo 和 bar.a 两个属性。当其中任意一个属性的值发生变化时,其对应的依赖都会被更新,从而触发其绑定的观订阅者的回调函数。

简单来说,在 Vue2 响应式系统中,当数据发生改变时,会触发 get 和 set 方法,get 方法会收集所有依赖该数据的 Watcher 对象,set 方法会通知 Dep 对象触发所有 Watcher 对象的回调函数进行更新。如此循环,实现了数据的响应式。

在这里插入图片描述

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

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

相关文章

Windows安装MongoDB结合内网穿透轻松实现公网访问本地数据库

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

基于SpringBoot的“银行OA系统的设计与实现”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“银行OA系统的设计与实现”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 用户登录界面 管理员主界面 员工基本档…

武汉星起航顺应政策东风,打造跨境电商孵化新标杆

在国家政策的鼎力支持下&#xff0c;跨境电商行业迎来了蓬勃发展的黄金时期。武汉星起航电子商务有限公司作为行业的佼佼者&#xff0c;积极响应国家政策号召&#xff0c;凭借专业的运营团队和丰富的经验&#xff0c;成功打造了一站式的跨境电商亚马逊孵化平台&#xff0c;为合…

element-ui form表单自定义label的样式、内容

element-ui form表单自定义label的样式、内容 效果截图 代码 <el-form size"small" :inline"true" label-width"120px"><el-form-item prop"name"><div slot"label"><i style"color: red;"…

JavaWeb--JavaScript-事件绑定/BOM/DOM编程

目录 1. 事件绑定 1.1. 什么是事件 1.2. 常见事件 1.3. 事件的绑定 1.3.1. 属性绑定 1.3.2. DOM编程绑定 1.4. 事件的触发 1.4.1. 行为触发 1.4.2. DOM编程触发 2. BOM 编程 2.1. 什么是 BOM 2.2. window对象的常见属性(了解) 2.3. window对象的常见方法(了解) 2…

【中文医疗词嵌入模型】SMedBERT:结构化知识图谱 + 混合注意力机制 + 提及-邻居上下文建模

【中文医疗词嵌入模型】SMedBERT&#xff1a;结构化知识图谱 混合注意力机制 提及-邻居上下文建模 提出背景SMedBERT 具体到点的设计逻辑SMedBERT的背景SMedBERT的工作原理 SMedBERT 具体实现细节3.1 符号和模型3.2 Top-K Entity Sorting3.3 提及-邻居混合注意力3.4 提及-邻居…

Android - 安卓概述

什么是安卓? Android 是一种基于 Linux 的开源操作系统&#xff0c;适用于智能手机和平板电脑等移动设备。 Android 是由 Google 和其他公司领导的 Open Handset Alliance 开发的。 Android 为移动设备的应用程序开发提供了统一的方法&#xff0c;这意味着开发人员只需为 And…

【JavaWeb】Day42.MySQL概述——多表查询

介绍 多表查询&#xff1a;查询时从多张表中获取所需数据 单表查询的SQL语句&#xff1a;select 字段列表 from 表名; 如果要执行多表查询&#xff0c;只需要使用逗号分隔多张表即可&#xff0c;如&#xff1a; select 字段列表 from 表1, 表2; 例如&#xff1a;查询…

C++11的更新介绍(新的类功能、可变参数模板)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 新的类功能 默认成员函数&…

《师兄啊师兄》:玄机科技打造国漫新高峰,IP运营再显神力

在这个国漫蓬勃发展的时代&#xff0c;玄机科技再次以其超凡的制作水准和出色的IP运营能力&#xff0c;为我们带来了一部国漫新经典——《师兄啊师兄》。这部作品不仅在画面、剧情上达到了行业新高度&#xff0c;更在IP运营上展现出了其强大的实力与前瞻性。 《师兄啊师兄》的画…

构建智能连接的未来:物联网平台系统架构解析

随着科技的不断进步和互联网的普及&#xff0c;物联网&#xff08;Internet of Things, IoT&#xff09;已成为连接世界的新方式。物联网平台作为实现物联网应用的核心基础设施&#xff0c;其系统架构的设计和实施至关重要。本文将深入探讨物联网平台系统架构的关键要素和最佳实…

【七 (2)FineBI-平台新增用户留存分析】

目录 文章导航一、字段解释1、用户平台登录信息表格2、用户平台激活信息表格 二、需求三、操作步骤1、建立用户平台登录信息表格和用户平台激活信息表格的关联关系2、将用户平台激活信息表格的激活日期添加到用户平台登录信息表格3、新增公式列&#xff0c;计算激活时间和登录时…

P4119 [Ynoi2018] 未来日记

它来了&#xff01; 分析一下第一个操作&#xff0c;不是写过嘛&#xff0c;并查集 分析一下第二个操作&#xff0c;二分套二分答案 拿下了这题 仔细分析&#xff0c;貌似时间复杂度是错的 我们考虑块套块 时间复杂度 对1e5的值域进行分块 求k值我们可以先找是第几个块&…

word并排比较

Word并排比较是一种在Microsoft Word文档中同时显示两个文本内容并进行比较的功能。这种比较通常用于查看文档的不同版本之间的差异&#xff0c;或者比较两个不同来源的文本内容。 在Word中进行并排比较通常可以通过以下步骤实现&#xff1a; 通过这种方式&#xff0c;Word的并…

2024阿里云学生服务器申请图文全流程,学生机免费续费攻略

2024年阿里云学生服务器免费申请&#xff0c;完成学生认证可以领取1个月免费学生机&#xff0c;完成任务可以再免费学费6个月时长&#xff0c;还可以领取高校计划学生300元无门槛优惠代金券&#xff0c;阿里云服务器网aliyunfuwuqi.com整理2024年最新阿里云大学生服务器申请入口…

8路HDMI+8路AV高清视频流媒体编码器JR-3218HD

产品简介&#xff1a; JR-3218HD高清音视频编码产品支持8路高清HDMI音视频采集功能&#xff0c;8路AV视频采集功能&#xff0c;8路3.5MM独独立音频接口采集功能。编码输出双码流H.264格式&#xff0c;音频MP3/AAC格式。编码码率可调&#xff0c;画面质量可控制。支持HTTP/RTSP…

引人共鸣的情感视频素材在哪找?今天看这五个网站

朋友们好啊&#xff0c;最近是不是不少人都在发愁啊&#xff0c;优秀创作者做视频用的视频素材哪来的啊&#xff1f;今天我为朋友们准备了几个优秀的视频素材网站&#xff0c;让你们做视频不再缺少素材&#xff0c;然后还有几个辅助创作的工具&#xff0c;都是你们需要的&#…

电源——BUCK详解

目录 BUCK电路工作原理3种工作模式 BUCK电路实操PCB如何降低EMI和EMC及注意事项 BUCK电路工作原理 如图&#xff0c;给一个一定频率的PWM波控制Q1&#xff0c;使得输入电压不停的导通断开&#xff0c;达到降压的目的。 输入电压 * 占空比 输出电压 非隔离 输入与输出的极性相同…

CSS导读 (复合选择器 上)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 二、CSS的复合选择器 2.1 什么是复合选择器 2.2 后代选择器(重要) 2.3 子选择器(重要) Questions 小提…

贝乐虎儿歌v6.8.0解锁高级版亲子学习儿歌

软件介绍 贝乐虎儿歌免费版app&#xff0c;出自乐擎网络的创意工坊&#xff0c;专为孩子们雕琢了一系列富含创意的动画儿歌内容。这款app通过贝乐虎兄弟的可爱形象&#xff0c;让孩子们在愉快的观看中接触到各种儿歌和故事。不仅如此&#xff0c;app还巧妙地将古诗、英语等学习…