当前位置: 首页 > 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.jsqmd.com/news/986758/

相关文章:

  • 中兴交换机堆叠配置保姆级教程:从端口关闭到重启上线的完整流程
  • Placement-Preparation求职全攻略:从简历准备到面试技巧的完整指南
  • STM32CubeMX配置SPI驱动W25Q64,从零到读写测试的保姆级避坑指南
  • 开源大模型2024生产选型实战:推理效率、硬件适配与中文落地
  • 2026液冷系统排液阀源头工厂推荐:液冷管截止阀全品类生产厂家实力解析 - 栗子测评
  • 盐城边牧,法斗,德牧哪家店比较好,2026精选宠物店排行榜推荐 - 谊识预商务
  • 用MATLAB复现四通道麦克风阵列TDOA定位:从数据集构建到双曲线交汇算法实战
  • AI 推广公司哪家好?2026 实测对比 - 新闻快传
  • `javax.xml.validation` 是 Java 标准版(Java SE)中用于 XML 文档验证的核心包
  • 2026年郑州短视频代运营与GEO优化推广服务商深度横评指南 - 企业名录优选推荐
  • 保姆级教程:用STM32F103驱动ST7735屏幕显示高清图片(附Python图片转换脚本)
  • 保姆级教程:用NVIDIA SDK Manager给Jetson Xavier NX刷机,附99%卡住、SSD启动失败等常见问题解决
  • 什么牌子素颜霜最好用?盘点2026好用又自然的素颜霜口碑榜 - 新闻快传
  • MySQL5.7免安装教程
  • 告别虚拟机!用Docker在Mac/Windows上5分钟搞定Oracle 19c开发环境
  • 多项式插值原理与工程实践:从穿点拟合到龙格现象规避
  • REFramework兼容性问题深度解析:5步解决《怪物猎人:荒野》崩溃难题
  • 2026 年 6 月武汉黄金回收|添价收黄金奢侈品回收中心,专业估价诚意出价 - 薛定谔的梨花猫
  • 别再只调参了!深入SENet消融实验,揭秘通道注意力超参数(如压缩比r)的实战影响
  • 从Sort到DeepSORT:我是如何用‘外观特征’解决目标跟踪中ID频繁跳变这个老大难问题的
  • 音乐歌词获取利器:一键解决你的歌词烦恼,高效管理音乐库
  • 告别玄学调参:用ADS负载/源牵引一步步优化你的2400MHz功放效率(附完整Harmonic Balance设置)
  • 告别2003错误:在CentOS 7上为Navicat配置MySQL远程访问的完整指南
  • `javax.xml.rpc.holders` 是 JAX-RPC(Java API for XML-Based RPC)规范中的一个包
  • 构建企业级语音识别系统:Whisper Base英文模型深度解析与实践指南
  • BlazorFluentUI核心组件解析:打造Windows 11风格的Blazor应用
  • OLTP到Data Lakehouse:构建实时可信分析底座
  • 保姆级教程:用Qt Designer和C++为你的软件添加“设置”窗口(含菜单栏信号连接、模态对话框与QML交互)
  • yuzu模拟器版本选择与管理:5个实战技巧告别版本混乱
  • Vivado IP核综合失败别慌:除了打补丁,这个TCL命令也能救急(以Video Frame Buffer为例)