简单介绍一下Vue中的响应式原理

自从 vue 发布以来,就受到了广大开发人员的青睐,提到 vue,我们首先想到的就是 vue 的响应式系统,那响应式系统到底是怎么回事呢?接下来我就给大家简单介绍一下 vue 中的响应式原理。
vue2 的响应式原理
尽管 vue2 将于 2023 年 12 月 31 日停止维护,但是我们依然有很多项目是基于 vue2.x 进行开发的,那么我们先简单看一看 vue2.x 是基于什么实现的吧~
object.defineproperty
vue2 的响应式原理是基于对象的 defineproperty () 方法进行开发的,那么这个方法有什么作用呢?mdn 是这样介绍的:  
object.defineproperty () 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
也就是说,我们可以通过对象的这个方法精确的添加或者修改对象的属性。每个对象都具有 get/set 属性,当访问 get 属性时,会调用 getter 方法,当对象的属性值被修改时,会调用 setter 方法,正式基于 getter 和 setter 方法,vue 才可以利用 object.defineproperty 来实现响应式系统。
object.defineproperty 在 vue 中的使用
在 vue 中,当把一个普通的 javascript 对象传入 vue 实例作为 data 选项,vue 会遍历此对象的所有属性,并使用 object.defineproperty 将这些属性转为 getter/setter, getter/setter 可以追踪依赖,在属性被访问的时候通知视图变更。
object.defineproperty(obj, 'targetobj', { get() { // 完成依赖收集 }, set() { // 发生变更,同时通知相关依赖 }})  
vue3 的响应式原理
vue2.0 很好的实现了数据的双向绑定,但是也遗留了一个很重要的问题:由于 vue 会在初始化实例时将 property 转化为 getter/setter,所以,property 必须在 data 对象上先存在才能让 vue 将其转换为响应式数据。
那么对于新增加的对象、或者某些需要特殊操作的数组想要转换为响应式数据就需要使用 vue.set 等方法。 vue3 就很好的解决了这个问题。那么,vue3 是如何解决的呢?让我们就一起看看吧~
proxy
提到 vue3 的数据拦截,我们首先要了解什么是 proxy?
proxy 可以理解成,在目标对象之前架设一层 “拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。proxy 这个词的原意是代理,用在这里表示由它来 “代理” 某些操作,可以译为 “代理器”。
原来,vue3 用了 proxy 代理代替了 object.defineproperty 方法。同样的,在 proxy 中也有 get/set 方法,举个例子~
var obj = new proxy({}, { get: function (target, name) { return name; }, set: function (target, key, val) { target[key] = val return target; }});我们通过给每一个目标对象都建立一个对应的 proxy 对象对其代理就可以弥补 object.defineproperty 对于新增对象无法监听的缺陷。  
简单设计一个 vue3 的响应系统
实现一个简单的响应系统的思路: ・读取(get)时,将副作用函数入栈; ・设置(set)时,将副作用函数出栈,执行副作用函数。
// 存储副作用函数的栈const bucket = new set()// 存储被注册的副作用函数let activeeffect// 注册副作用函数functioneffect (fn) { // 存储副作用函数 activeeffect = fn fn()}// 副作用函数fneffect ( () => { document.body.innertext = obj.text })执行匿名函数 fn 方法时,会触发响应式数据 obj.text 的读取操作,进而触发代理对象 proxy 的 get 拦截函数:const proxy = new proxy(data, { get (target, key) { if (activeeffect) { bucket.add(activeeffect) } return target[key] }, set (target, key, newval) { target[key] = newval bucket.foreach(fn => fn()) return true }})到此,我们会发现,有一个疑问,我们怎样能保证修改一个属性之后触发的副作用函数是我预期想要触发的副作用函数呢?为了解决这个问题,我们还需要建立副作用函数与目标对象的联系: 我们仅需要用 weakmap 代替 set 数据结构:const bucket = new weakmap()  
修改 proxy 对象:
const proxy = new proxy(data, { get (target, key) { if (!activeeffect) return target[key] // 先从栈中取出depsmap,depsmap中保存目标对象和其相关副作用函数的一对多的关系 let depsmap = bucket.get(target) if (!depsmap) { bucket.set(target, (depsmap = new map()) } // 再根据key从depsmap中取得deps,deps保存所有与key相关联的副作用函数 let deps = depsmap.get(key) if (!deps) { depsmap.set(key, (deps = new set()) } deps.add(activeeffect) return target[key] }, set (target, key, newval) { target[key] = newval const depsmap = bucket.get(target) if (!depsmap) return const effects = depsmap.get(key) effects && effects.foreach(fn => fn()) } })这样,我们就实现了一个简易的响应系统。那么为什么要用 weakmap 而不是使用 map 呢?就交给大家一起思考啦~


瑞芯微CES2019发布AIoT芯片RK1808,内置高能效NPU
COMS-Magview-磁光传感器技术为磁材料测量领域提供了全新的视角!
可拉伸透明电容式应变传感器用于精准监测不同类型的人体活动
新闻 | 罗姆发布2022财年财报,销售额连续两年创新高
华北工控嵌入式计算机主板,为医疗发展提供强大动力
简单介绍一下Vue中的响应式原理
电源芯片U6335自带VDD低压自供电功能
清华大学校友捐赠大事件 AI独角兽深鉴科技捐赠3400万
自动驾驶周刊:立得空间分立自动驾驶高精地图,发力B2B商用地图服务
丽江古城搭建5G无人商店等应用,为游客带来实用便捷的智慧服务
Too many open files错误导致服务器死循环
DxOMARK:OPPO Reno 10倍变焦版相机评分即将公布
采用LM836的LED数码管驱动电路原理分析
瑞典公开宣布封杀华为,华为强势回击:必须上诉
当下人工智能行业的发展前景如何?
oppor11、金立s10国产苹果手机,这种拿来主义还能持续多久?
机组启动时胀差变化的分析与控制
联想19/20财年第一季度业绩迎来开门红
荣耀Magic2深度评测 非常值得入手
英语六级考试资料免费领!价值千元考试干货!