Vue3.0采用新特性Proxy来实现数据状态的响应,它的原理是啥?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue3.0采用新特性Proxy来实现数据状态的响应,它的原理是啥?相关的知识,希望对你有一定的参考价值。
参考技术A
Vue3 使用了 Proxy 替换了原来的 Object.defineProperty 来实现数据响应。
很简单,直接Vue引入reactive方法,接收一个对象参数,就实现了数据的响应式:
reactive 内部的核心代码 简化 如下:
首先判断传入的参数类型是否可以用于观察,目前支持的类型为 Object|Array|Map|Set|WeakMap|WeakSet。
接下来判断参数的构造函数,根据类型获得不同的 handlers。这里我们就统一使用 baseHandlers ,因为这个已经覆盖 99% 的情况了。只有 Set, Map, WeakMap, WeakSet 才会使用到 collectionHandlers。
对于 baseHandlers 来说,最主要的是劫持了 get 和 set 行为,这两个行为同时也能原生劫持 数组下标修改值及对象新增属性的行为, 这一点非常重要,因为 Object.defineProperty 就不行。
最后就是构造一个 Proxy 对象完成数据的响应式。相比 Object.defineproperty 一开始就要 递归遍历整个对象 的做法来说,使用 Proxy 性能会好得多。比如原来 forEach 遍历:
接下来当我们去使用 state 对象的时候,就能劫持到内部的行为。
读取时:state.num 就会触发 get 函数;
修改时:state.num = 100 就会触发 set 函数。
以下是这两个函数的核心(TS语法):
对于 get 函数来说,获取值肯定是最核心的一步骤了。接下来是调用 track,这个和 effect 有关,下文再说。最后是判断值的类型,如果是对象的话就继续包装成 Proxy。
对于 set 函数来说,设置值是第一步骤,然后调用 trigger,这也是 effect 中的内容。
简单来说,如果某个 effect 回调中有使用到 state.num,那么这个回调会被收集起来,并在调用 state.num =100 时触发。
那么怎么收集这些内容呢?这就要说说 targetMap 这个对象了。它用于存储依赖关系,类似以下结构,这个结构会在 effect 文件中被用到
先来解释下三者到底是什么,这个很重要 :
这里笔者把这些内容脱离源码串起来讲一下流程。
首先创建一个 Proxy 对象,targetMap 会把这个对象收集起来当做 key。
接下来调用 effect 回调的时候会把这个回调保存起来,用于下面的依赖收集。在调用的过程中会触发 counter 的 get 函数,内部调用了 track 函数,这个函数会使用到 targetMap。
这里首先通过 target 从 targetMap 中取到一个对象,这个对象也就是 target 所有的依赖关系。那么对于 counter.num 来说,num 就是这个对象的 key(这里如果有点模糊的话可以先看下上面的数据结构),值是一个依赖回调的集合,因为 counter.num 可能会被多个地方依赖到。
回调执行完毕以后会把保存的回调销毁掉。
当我们调用 counter.num = 7 时,触发 set 函数,内部调用 trigger 函数,同样会使用到 targetMap。
同样通过 target 取到一个对象,然后通过 key 也就是 num 去取出依赖集合,最后遍历这个集合执行里面所有的回调函数。
另外对于 computed 来说,内部也是使用到了 effect,无非它的回调不会在调用 effect 后立即执行,只有当触发 get 行为以后才会执行回调并进行依赖收集,举个例子:
对于以上代码来说,computed 的回调永远不会执行,只有当使用到了 cValue.value 时才会执行回调,然后接下来的操作就和上面的没区别了。
Proxy(vue响应式原理:数据侦测--数据劫持和数据代理)
参考技术A Object.defineProperty : 通过设定对象属性getter/setter方法来监听数据的变化,同时getter也用于依赖收集,而setter在数据变更时通知订阅者更新视图。1.无法检测到对象属性的新增或删除
由于js的动态性,可以为对象追加新的属性或者删除其中某个属性,这点对经过Object.defineProperty方法建立的响应式对象来说,只能追踪对象已有数据是否被修改,无法追踪新增属性和删除属性,这就需要另外处理。
2.不能监听数组的变化
vue在实现数组的响应式时,它使用了一些hack,把无法监听数组的情况通过重写数组的部分方法来实现响应式,这也只限制在数组的push/pop/shift/unshift/splice/sort/reverse七个方法,其他数组方法及数组的使用则无法检测到。
Proxy,字面意思是代理,是ES6提供的一个新的API,用于修改某些操作的默认行为,可以理解为在目标对象之前做一层拦截,外部所有的访问都必须通过这层拦截,通过这层拦截可以做很多事情,比如对数据进行过滤、修改或者收集信息之类。借用 proxy的巧用 的一幅图,它很形象的表达了Proxy的作用。
ES6原生提供的Proxy构造函数,用法如下:
其中obj为Proxy要拦截的对象,handler用来定制拦截的操作,返回一个新的代理对象proxy;Proxy代理特点:
1.Proxy的代理针对的是整个对象,而不是像Object.defineProperty针对某个属性。只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
2.Proxy也可以监听数组的变化
参考: https://juejin.cn/post/6850418111985352711
以上是关于Vue3.0采用新特性Proxy来实现数据状态的响应,它的原理是啥?的主要内容,如果未能解决你的问题,请参考以下文章