当前位置: 首页 > news >正文

Vue2响应式原理详解——简单易理解

首先,我希望你能够读完,因为他确实对你有一点帮助。
我给大家准备了一张便于理解的图

话不多说,我们直接上干货。

首先看下Vue的构造函数

classVue{constructor(options={}){this.el=options.elthis.exp=options.expthis.data=options.data el.innerHTML=this.data[this.exp]// 初始化页面内容letobserver=newObserver()observer.defineReactive(this.data)newWatcher(this,this.exp,function(val){//创建watcher实例调用构造函数。el.innerHTML=val})returnthis}}

从构造函数当中我们可以得到的信息如下:

  1. 首先是对this.data进行了observer的操作
  2. new Watcher将当前的this传了进去

Observer是什么?defineReactive又是什么?

先来看下Observer,代码如下:

exportclassObserver{constructor(value){this.value=value;if(!Array.isArray(value)){this.walk(value);}}walk(obj){constkeys=Object.keys(obj);for(leti=0;i<keys.length;i++){defineReactive(obj,keys[i],obj[keys[i]]);}}}functiondefineReactive(data,key,val){// 新增,递归子属性if(typeofval==="object"){newObserver(val);}letdep=newDep();Object.defineProperty(data,key,{enumerable:true,configurable:true,get:function(){dep.depend();// *******returnval;},set:function(newVal){if(val===newVal){return;}val=newVal;dep.notify();// -------},});}

可以看到Observer对象当中有一个函数叫做defineReactive,刨析这个函数可以我们可以看到其实是Object.defineProperty的一个封装操作。我们知道Object.defineProperty是对传入的参数对象进行递归遍历并为该对象的每一个属性添加对应的setget方法,也就是我们熟知的数据劫持,或者称为数据拦截

再然后我们知道setget方法都有它各自的作用get当中收集依赖,而set当中触发依赖。我们再看发现他在这里进行了new dep的操作。dep对象又是用来做什么的呢?我们接着看。

exportdefaultclassDep{constructor(){this.subs=[];}addSub(sub){// 收集依赖this.subs.push(sub);}removeSub(sub){// 移除依赖remove(this.subs,sub);}// depend通过判断window.target也就是当前的watcher实例对象是否存在调用addSubdepend(){if(window.target){this.addSub(window.target);}}notify(){// 通知也就是数据改变之后的更新操作constsubs=this.subs.slice();for(leti=0,l=subs.length;i<l;i++){subs[i].update();// update方法在watcher当中}}}functionremove(arr,item){// 移除依赖if(arr.length){constindex=arr.indexOf(item);if(index>-1){returnarr.splice(index,1);}}}

我们看到在defineReactive这个方法的get方法中调用了dep.depend();在方法有*号标注的地方。然后我们回到class Dep当中,首先是初始化声明了一个依赖数组,然后在depend方法中判断了一下window.target是否存在,存在则调用addSub添加依赖。到这里很多人其实会比较疑惑window.target是什么,我先说一下就是下面要将到的Watcher对象,现在你只需要把它看成一个依赖就可以了。

再看set方法,调用了dep.notify();,同样的我们去找一下这个方法,他对当前的一个subs进行了循环调用subs当中每一项的update方法。update方法是watcher对象的一个方法,到这里Vue构造函数当中的observer.defineReactive(this.data)这一步就已经结束了 。到这里的话其实我们已经为传入的this.data添加了对应的setget方法,但是并没有收集到对应的依赖,因为此时window.target的值一直是undefined

现在所有的准备工作都已经做好了。只缺少了一个关键点去触发,而这个关键点就在watcher当中。最后我们需要看一下这个Watcher了。

Watcher又是什么?

exportdefaultclassWatcher{constructor(vm,expOrFn,cb){this.vm=vm;// 执行this.getter(),就可以读取data.a.b.c的内容this.getter=parsePath(expOrFn);this.cb=cb;this.value=this.get();}get(){window.target=this;// 这就与上面联系起来了,this指向当前的实例对象letvalue=this.getter.call(this.vm,this.vm);// 获取当前的valuewindow.target=undefined;returnvalue;}update(){constoldValue=this.value;this.value=this.get();this.cb.call(this.vm,this.value,oldValue);}}

我们看Watcher发现他会在构造函数constructor当中读取传入的data的值。详细看get方法内容会发现此时的this肯定就是当前的watcher实例对象了对吧,再看Watcher对象的get方法第一句就是window.target = this,将window.target的值指向了当前的Watcher

然后let value = this.getter.call(this.vm, this.vm);,此时去读取了值。要知道,在进行这一步之前我们就已经为data添加了数据拦截,那么这个时候我们去读取它的值肯定会触发拦截。也就是defineReactive当中的get方法里面的dep.depend。由此收集到了对应的依赖数组,当数据发生变化的时候触发set方法,循环对应的依赖数组,调用其update方法触发依赖使得数据视图发生变化。

到此就结束了,这个还是需要大家多去串联一下他们之间的关系才能融会贯通。好了,下课!!!

http://www.rkmt.cn/news/1498507.html

相关文章:

  • 中兴交换机堆叠配置保姆级教程:从端口关闭到重启上线的完整流程
  • 动手算一算:如何用Python快速估算光纤通信系统的最大传输距离?
  • 用PyTorch手把手实现DDPG算法,搞定OpenAI Gym连续控制任务(附完整代码)
  • `javax.xml.validation` 是 Java 标准版(Java SE)中用于 XML 文档验证的核心包
  • 用MATLAB复现四通道麦克风阵列TDOA定位:从数据集构建到双曲线交汇算法实战
  • 告别虚拟机!用Docker在Mac/Windows上5分钟搞定Oracle 19c开发环境
  • 2026 年 6 月武汉黄金回收|添价收黄金奢侈品回收中心,专业估价诚意出价 - 薛定谔的梨花猫
  • 从Sort到DeepSORT:我是如何用‘外观特征’解决目标跟踪中ID频繁跳变这个老大难问题的
  • Vivado IP核综合失败别慌:除了打补丁,这个TCL命令也能救急(以Video Frame Buffer为例)
  • 从Mega2560迁移到STM32F407:在PlatformIO中为你的3D打印机升级Marlin 2.0固件
  • FFmpeg-Builds终极配置指南:5分钟掌握跨平台编译核心技巧
  • 扩散Transformer技术演进:从DiT到SiT的数学原理与架构创新深度解析
  • 注意力机制在语音增强中的应用:Awesome-Speech-Enhancement中的Transformer与Multi-Head Attention终极指南 [特殊字符]
  • 无线环境透视:ESP-CSI让ESP32拥有环境感知超能力
  • DexKit API参考手册:从基础查询到高级匹配的完整指南
  • `javax.xml.transform.stream` 是 Java 标准库中用于 XML 转换(XSLT)的流式输入/输出支持包
  • 盘点昆明本地正规家装品牌 最新实测十家靠谱装修公司附完整选装指南 - 装修新知
  • 动态随机块模型中的嵌入生死过程研究与应用
  • 开发常见的http状态码.——400,401,403,404,500,501,503,状态码大全!
  • KKGridView性能优化指南:达到55+FPS的秘诀
  • 2026考生必看:重庆城市职业学院有哪些王牌专业?什么专业好就业? - 品牌2026
  • 保姆级教程:用示波器+电流钳实测汽车轮速传感器AK协议信号(含数据解析步骤)
  • 组织架构树形选择组件使用说明(Vue3 + UniApp)
  • 如何快速上手clianpro超链PRO:10分钟掌握网盘直链解析技巧
  • 2026成都留学中介排名,八家优选测评前三强品牌 - 资讯速览
  • 2026广州名表回收避坑实录:5家店亲测,收的顶不压价稳居C位 - 奢侈品回收评测
  • SAP各模块BAPI合集
  • 河北球场围栏网厂家排行:实测资质与交付能力对比 - 奔跑123
  • GWSL终极指南:在Windows上轻松运行Linux图形应用
  • MobileOne重参数化技术详解:如何将多分支网络转换为单分支推理