Vue3 中的数据侦测的实现(4)

深度侦测数据是通过 createGetter 函数实现的,前面提到,当对多层级的对象操作时,set 并不能感知到,但是 get 会触发,于此同时,利用 Reflect.get() 返回的“多层级对象中内层” ,再对“内层数据”做一次代理。

function createGetter() { return function get(target, key, receiver) { const res = Reflect.get(target, key, receiver) return isObject(res) ? reactive(res) : res } }

可以看到这里判断了 Reflect 返回的数据是否还是对象,如果是对象,则再走一次 proxy ,从而获得了对对象内部的侦测。

并且,每一次的 proxy 数据,都会保存在 Map 中,访问时会直接从中查找,从而提高性能。

当我们打印代理后的对象时:

Vue3 中的数据侦测的实现

可以看到这个代理后的对象内层并没有代理的标志,这里仅仅是代理外层对象。

输出其中一个存储代理数据的 rawToReactive :

Vue3 中的数据侦测的实现

对于内层 ary: [1, 2] 的代理,已经被存储在了 rawToReactive 中。

由此实现了深度的数据侦测。

问题二:如何避免多次 trigger ?

function hasOwn(val, key) { const hasOwnProperty = Object.prototype.hasOwnProperty return hasOwnProperty.call(val, key) } function set(target, key, val, receiver) { console.log(target, key, val) const hadKey = hasOwn(target, key) const oldValue = target[key] val = reactiveToRaw.get(val) || val const result = Reflect.set(target, key, val, receiver) if (!hadKey) { console.log('trigger ... is a add OperationType') } else if(val !== oldValue) { console.log('trigger ... is a set OperationType') } return result }

关于多次 trigger 的问题,vue 处理得很巧妙。

在 set 函数中 hasOwn 前打印 console.log(target, key, val) 。

输入:

let data = ['a', 'b'] let r = reactive(data) r.push('c')

输出结果:

Vue3 中的数据侦测的实现

r.push('c') 会触发 set 执行两次,一次是值本身 'c' ,一次是 length 属性设置。

设置值 'c' 时,传入的新增索引 key 为 2,target 是原始的代理对象 ['a', 'c'] ,hasOwn(target, key) 显然返回 false ,这是一个新增的操作,此时可以执行 trigger ... is a add OperationType 。

当传入 key 为 length 时,hasOwn(target, key) ,length 是自身属性,返回 true,此时判断 val !== oldValue , val 是 3, 而 oldValue 即为 target['length'] 也是 3,此时不执行 trigger 输出语句。

所以通过 判断 key 是否为 target 自身属性,以及设置val是否跟target[key]相等 可以确定 trigger 的类型,并且避免多余的 trigger。

总结

实际上本文主要集中讲解 Vue3 中是如何使用 Proxy 来侦测数据的。而在分析源码之前,需要讲清楚 Proxy 本身的一些特性,所以讲了很多 Proxy 的前置知识。同时,我们也通过自己的方式来解决这些问题。

最后,我们对比了 Vue3 中,是如何处理这些细节的。可以看出,Vue3 并非简单的通过 Proxy 来递归侦测数据,而是通过 get 操作来实现内部数据的代理,并且结合 WeakMap 来对数据保存,这将大大提高响应式数据的性能。

有兴趣的小伙伴可以针对 递归Proxy 和 这种Vue3的这种实现方式做相应的 benchmark ,这两者的性能差距比较大。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/aad2a5041fd247d995a4c51caea8fcec.html