阅读 380

五分钟看懂Vue3-数据绑定

Vue3在伟大祖国70周岁时终于和大家见面了。🚩


在学习Vue3的数据绑定前,先预习一下相关知识:

1、Proxy,我之前写过一篇Proxy介绍,可以参考下。

2、WeakMap

然后我们就能愉快的玩耍了。

Vue3中关于数据绑定这块的代码主要在这里


我们主要关注reactive.ts和baseHandlers.ts这两个文件里的内容。

1.Proxy已知的两个问题

  • Proxy本身不支持对象内部的深度侦测,需要自己实现

  • Proxy本身支持数组变化侦测,但会有多次触发的风险

2.Vue3是如何解决

2-1

解决第一个问题,对于初学者,我们可以采用暴力递归的办法,一开始就把对象内部的所有对象都用Proxy代理一遍。


这样操作的结果就是,proxy变成下图所示的结果:


可以看到,对象内部的对象和数组都已经被代理了。当obj是一个非常大且复杂的对象时,这样做性能肯定不好,我们来看看Vue是怎么做的。

Vue中,一开始我们只是把最外层的对象做Proxy操作,而内层的对象,只有在需要访问的时候,才会惰性地创建Proxy代理。为此,我们需要对handler函数做一下改造。


完成上述操作后,Vue3还做了一件事,就是把动态创建的Proxy都存在一个WeakMap中,这样我们就不用每次执行get操作的时候都会去动态创建了,毕竟这是很耗费内存的。在Vue3源码中我们可以有两个WeakMap,就是干这事的。为此,我们还需要改造一下createReactiveObject方法,传入两个WeakMap来做缓存。另外,toRaw这个WeakMap干嘛用的,留给大家思考一下😊。


2-2

下面我们来解决第二个问题,假设我们有一个数组arr=[1,2],当我们执行arr.push(3)时,arr有两个属性发生了变化,一个是新增了一个key=2的索引属性,对应value就是我们刚添加的3,这时可以把数组理解成一个对象;此外arrlength属性也发生了变化,增加了1。所以,如果用Proxy劫持了数组的set方法时,此时会触发两次set操作,一次对应的key是2,一次对应的key是length


每次执行set操作新增或者修改某个属性时,我们先利用Object.hasOwnProperty判断这个属性是否存在,如果不存在则直接trigger add ...;如果是已有属性,则判断当前valuetarget的上相同key对应的value,也就是代码中的oldValue是否相等,如果不相等,才会去执行trigger set ...。这里需要细细理解一下,当触发keylength的回调时,由于此时的target,就是被代理的数组对象,在上一次key2的回调触发时,执行了Reflect.set(target, key, value, receiver),此时它的数组长度已经+1了,所以在keylength的回调中,此时oldValuevalue是相等的,都是3。这样,我们就避免了多次触发render的操作,Vue3中的这段代码还是写的很精妙的,🐂🍺。

到此,Proxy原生的两个大问题都被合理解决了,接下来就是一些常规的前置条件判断,Vue3中只对数组、对象、Map、WeakMap、Set、WeakSet这几类数据做了数据绑定,对基本数据类型和一些JS内置对象如Date、Promise和Reg等是直接返回不会做Proxy处理的。

下面我们看一眼本文中用到的demo和Vue3源码的对比,可以看到,大体的结构是一样的,当然demo中做了很多的简化操作。


3.demo小结

看到这里,你是不是对Vue3中的数据绑定有了一个大概的了解。完整demo代码在这里。写的不完善的地方,还请各位大佬多多指教。