一文搞懂 Vue3.0 为什么采用 Proxy

网站建设4年前发布
60 0 0

作用:在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。,1. 基本使用,语法:​Object.defineProperty(obj, prop, descriptor)​​,参数:,看一个简单的例子,通过这种方法,我们成功监听了person上的name属性的变化。,2.监听对象上的多个属性,上面的使用中,我们只监听了一个属性的变化,但是在实际情况中,我们通常需要一次监听多个属性的变化。,这时我们需要配合Object.keys(obj)进行遍历。这个方法可以返回obj对象身上的所有可枚举属性组成的字符数组。(其实用for in遍历也可以)
下面是该API一个简单的使用效果:
,利用这个API,我们就可以遍历劫持对象的所有属性 但是如果只是上面的思路与该API的简单结合,我们就会发现并达不到效果,下面是我写的一个错误的版本:,看起来感觉上面的代码没有什么错误,但是试着运行一下吧~你会和我一样栈溢出。这是为什么呢?让我们聚焦在get方法里,我们在访问person身上的属性时,就会触发get方法,返回person[key],但是访问person[key]也会触发get方法,导致递归调用,最终栈溢出。,这也引出了我们下面的方法,我们需要设置一个中转Obsever,来让get中return的值并不是直接访问obj[key]。,那么我们如何解决对象中嵌套一个对对象的情况呢?其实在上述代码的基础上,加上一个递归,就可以轻松实现啦~,我们可以观察到,其实Obsever就是我们想要实现的监听函数,我们预期的目标是:只要把对象传入其中,就可以实现对这个对象的属性监视,即使该对象的属性也是一个对象。,我们在defineProperty()函数中,添加一个递归的情况:,当然啦,我们也要在observer里面加一个递归停止的条件:,其实到这里就差不多解决了,但是还有一个小问题,如果对某属性进行修改时,如果原本的属性值是一个字符串,但是我们重新赋值了一个对象,我们要如何监听新添加的对象的所有属性呢?其实也很简单,只需要修改set函数:,到这里我们就完成啦~,4.监听数组,那么如果对象的属性是一个数组呢?我们要如何实现监听?请看下面一段代码:,我们发现,通过​push​方法给数组增加的元素,set方法是监听不到的。,事实上,通过索引访问或者修改数组中已经存在的元素,是可以出发get和set的,但是对于通过push、unshift增加的元素,会增加一个索引,这种情况需要手动初始化,新增加的元素才能被监听到。另外,通过 pop 或 shift 删除元素,会删除并更新索引,也会触发 setter 和 getter 方法。,在Vue2.x中,通过重写Array原型上的方法解决了这个问题,此处就不展开说了,有兴趣的uu可以再去了解下~,是不是感觉有点复杂?事实上,在上面的讲述中,我们还有问题没有解决:那就是当我们要给对象新增加一个属性时,也需要手动去监听这个新增属性。,可以看到,通过Object.definePorperty()进行数据监听是比较麻烦的,需要大量的手动处理。这也是为什么在Vue3.0中尤雨溪转而采用Proxy。接下来让我们一起看一下Proxy是怎么解决这些问题的吧~,语法:​const p = new Proxy(target, handler)​​ ,参数:,通过Proxy,我们可以对​设置代理的对象​上的一些操作进行拦截,外界对这个对象的各种操作,都要先通过这层拦截。(和defineProperty差不多),先看一个简单例子,可以看出,Proxy代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定。,在上面使用Object.defineProperty的时候,我们遇到的问题有:,1.一次只能对一个属性进行监听,需要遍历来对所有属性监听。这个我们在上面已经解决了。
2. 在遇到一个对象的属性还是一个对象的情况下,需要递归监听。
3. 对于对象的新增属性,需要手动监听
4. 对于数组通过push、unshift方法增加的元素,也无法监听
,这些问题在Proxy中都轻松得到了解决,让我们看看以下代码。,检验第二个问题,在上面代码的基础上,我们让对象的结构变得更复杂一些。,可以看到成功监听到了children对象身上的name属性(至于为什么children.height是undefined,可以再讨论一下),检验第三个问题,这个其实在基本使用里面已经提到了,访问的proxyObj.name就是原本对象上不存在的属性,但是我们访问它的时候,仍然们可以被get拦截到。,检验第四个问题,至此,我们之前的问题完美解决。,除了get和set来拦截读取和赋值操作之外,Proxy还支持对其他多种行为的拦截。下面是一个简单介绍,想要深入了解的可以去MDN上看看。,虽然Proxy完成了对目标对象的代理,但是它不是透明代理,也就是说:即使handler为空对象(即不做任何代理),他所代理的对象中的this指向也不是该对象,而是proxyObj对象。让我们来看一个例子:,可以看到,被代理的对象target内部的this指向了proxyObj。这种指向有时候就会导致问题出现,我们来看看下面一个例子:,在上面的例子中,由于jane对象的name属性的获取依靠this的指向,而this又指向proxyObj,所以导致了无法正常代理。,除此之外,有的js内置对象的内部属性,也依靠正确的this才能获取,所以Proxy 也无法代理这些原生对象的属性。请看下面一个例子:,可以看到,通过proxy代理访问Date对象中的getDate方法时抛出了一个错误,这是因为getDate方法只能在Date对象实例上面拿到,如果this不是Date对象实例就会报错。那么我们要如何解决这个问题呢?只要手动把this绑定在Date对象实例上即可,请看下面一个例子:,至此,我的总结就结束啦~ 文章不是很全面,还有很多地方没有讲到,比如:,学无止境,让我们一起努力叭~,参考文章:,1.Proxy 与Object.defineProperty介绍与对比,2.MDN Proxy

© 版权声明

相关文章