Vue3 双向绑定——Proxy

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue3 双向绑定——Proxy相关的知识,希望对你有一定的参考价值。

参考技术A 上一期 我用一个山寨的Vue class演示了vue响应式开发中双向绑定的实现。小结留了个尾巴——vue3将会用新的方式实现双向绑定。这一期就来介绍一下新的实现方式—— Proxy 。

回忆一下vue2响应式设计的实现:

vue2利用 Object.defineProperty 来劫持data数据的getter和setter操作。这使得data在被访问或赋值时,动态更新绑定的template模块。不过, Object.defineProperty 有一些天然的缺陷,而这些缺陷是es2015中Proxy可以解决的。我们下来慢慢介绍Proxy的解决之道。

在使用 Object.defineProperty 时,我们必须循环遍历所有的域值才能劫持每一个属性,说实在这就是个hack。

而Proxy的劫持手段则是官宣标准——直接监听data的所有域值。

Proxy构造函数的第一个参数是原始数据data;第二个参数是一个叫 handler 的处理器对象。Handler是一系列的代理方法集合,它的作用是拦截所有发生在data数据上的操作。这里的get()和set()是最常用的两个方法,分别代理 访问 和 赋值 两个操作。在Observer里,它们的作用是分别调用 dep.depend() 和 dep.notify() 实现订阅和发布。直接反映在Vue里的好处就是:我们不再需要使用 Vue.$set() 这类响应式操作了。除此之外,handler共有十三种劫持方式,比如 deleteProperty 就是用于劫持域删除。

尽管Proxy API还没Merge到Vue项目里,但是我们可以大概猜测一下它的实现。改写一下 上一版本的Observer

上一版本山寨VUE是这么构造的:

用Proxy改写后,稍微精简了一点, this.$data 由Observer作为工厂创建。

再跑一次上期的测试:

输出依旧是 10 200 10000 ,成功实现Proxy改造。

那么问题来了,为什么需要Proxy改造 Object.defineProperty 呢?

原因在于 Object.defineProperty 有先天缺陷——无法监听数组变化。而 Vue文档 提到它能检测如下八种数组操作操作;但很有趣的是: vm.items[0] = 1 这种操作是无法检测的。原因还是在于作者使用了hack的方式实现了这八种操作,而 vm.items[0] = 1 实在是hack不了了。

至于为什么当时非得使用 Object.defineProperty 而不是 Proxy ,原因还是浏览器兼容所限。至今IE仍不支持Proxy,polyfill也无法抹平。即便作者在重写vue3的时候,还是为原始浏览器保留了 Object.defineProperty 的实现。

Proxy在ES2015规范中正式发布,它是浏览器底层实现的一种对象 拦截器 ,原生支持JS数组操作(push、shift、splice等等)。

上面这个例子的打印结果是:

很显然,得利于浏览器原生支持,Proxy不需要各种hack技术就可以无压力监听数组变化;甚至有比hack更强大的功能——自动检测length。除此之外,Proxy还有多达 13种拦截方式 ,包括 construct 、 deleteProperty 、 apply 等等操作;而且性能也远优于 Object.defineProperty ,这应该就是所谓的新标准红利吧。

由于一些历史原因,vue只能使用 Object.defineProperty 实现双向绑定。这在当时是一种很前卫的设计,不过随着浏览器的不断迭代,这种技术在api和性能上愈发跟不上时代的步调。重写vue可以说是顺应历史潮流吧。

这是我写VUE源码设计的第三期,以后还会不定期更新。框架千变万化,但机理还是逃不过语言特性、数据结构和设计模式。我学习源码并没有什么功利的目的,想的还是拓展认知和巩固基础。当遇到特殊问题或是超过框架认知的需求时,其实最可靠的还是我们的基本功。

以上是关于Vue3 双向绑定——Proxy的主要内容,如果未能解决你的问题,请参考以下文章

Vue双向绑定原理 从vue2的Object.defineProperty到vue3的proxy

vue3.0(双向绑定)源码分析

vue3绑定proxy数据不更新

利用ES6中的Proxy和Reflect 实现简单的双向数据绑定

Vue3的双向绑定是如何实现的

面试题打卡第七天(vue2 vue3 原理篇)- 双向数据绑定