你了解vue3.0响应式数据怎么实现吗(3)

这个最根本的原因是因为这2种情况下,受制于js本身无法实现监听,所以官方建议用他们自己提供的内置api来实现,我们也可以理解到这里既不是defineProperty可以处理的,也不是包一层函数就能解决的,这就是2.x版本现在的一个问。 回到这篇文章的主题,vue官方会在3.x的版本中使用proxy来代替defineProperty处理响应式数据的过程,我们先来模拟一下实现,看看能否解决当前遇到的这些问题;

3.x版本

我们先来通过proxy实现对data对象的get和set的劫持,并返回一个代理的对象,注意,我们只关注proxy本身,所有的实现都是伪代码,有兴趣的同学可以自行完善

const defineReactiveProxyData = data => new Proxy(data, { get: function(data, key){ console.log(`getting ${key}`) return Reflect.get(data, key); }, set: function(data, key, newVal){ console.log(`setting ${key}`); if(typeof newVal === 'object'){ // 如果是object,递归设置代理 return Reflect.set(data, key, defineReactiveProxyData(newVal)); } return Reflect.set(data, key, newVal); } }) const data = { name: 'nanjing', age: 19 }; const vm = defineReactiveProxyData(data); vm.name // getting name nanjing vm.age = 20; // setting age 20

看起来我们的代理已经起作用啦,之后只要在setting的时候加上notify()去通知模板进行编译就可以了,然后我们来尝试设置一个数组看看;

vm.userIds = [1,2,3] // setting userIds vm.userIds.push(1); // getting userIds 因为我们会先访问一次userids // getting push 调用了push方法,所以会访问一次push属性 // getting length 数组push的时候 length会变,所以需要先访问原来的length // setting 3 通过下标设置的,所以set当前的index是3 // setting length 改变了数组的长度,所以会set length // 4 返回新的数组的长度

回顾2.x遇到的第一个问题,需要重新包装Array.prototype上的一些方法,使用了proxy后不需要了,解决了~,继续看下一个问题

vm.userIds.length = 2 // getting userIds 先访问 // setting length 在设置 vm.userIds[1] = '123' // getting userIds 先访问 // setting 1 设置index=1的item // "123"

从上面的例子中我们可以看到,不管是直接改变数组的length还是通过某一个下标改变数组的内容,proxy都能拦截到这次变化,这比defineProperty方便太多了,2.x版本中的第二个问题,在proxy中根本不会出现了。

总结1

通过上面的例子和代码,我们看到Vue的响应模式如果使用proxy会比现在的实现方式要简化和优化很多,很快在即将来临的3.0版本中,大家就可以体验到了。不过因为proxy本身是有兼容性的,比如ie浏览器,所以在低版本的场景下,vue会回退到现在的实现方式。

总结2

回归到proxy本身,设计模式中有一种典型的代理模式,proxy就是js的一种实现,它的好处在于,我可以在不污染本身对象的条件下,生成一个新的代理对象,所有的一些针对性逻辑放到代理对象上去实现,这样我可以由A对象,衍生出B,C,D…每个的处理过程都不一样,从而简化代码的复杂性,提升一定的可读性,比如用proxy实现数据库的ORM就是一种很好的应用,其实代码很简单,关键是要理解背后的思想,同时能够举一反三~

扩展: 1.Proxy.revocable()

这个方法可以返回一个可取消的代理对象

const obj = {}; const handler = {}; const {proxy, revoke} = Proxy.revocable(obj, handler); proxy.a = 1 proxy.a // 1 revoke(); proxy.a // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

一旦代理被取消了,就不能再从代理对象访问了

你了解vue3.0响应式数据怎么实现吗

打印proxy 可以看到IsRevoked变为true了

2.代理对象的this问题

因为new Proxy出来的是一个新的对象,所以在如果你在target中有使用this,被代理后的this将指向新的代理对象,而不是原来的对象,这个时候,如果有些函数是原对象独有的,就会出现this指向导致的问题,这种场景下,建议使用bind来强制绑定this

看代码:

const target = new Date(); const handler = {}; const proxy = new Proxy(target, handler); proxy.getDate(); // Uncaught TypeError: this is not a Date object.

因为代理后的对象并不是一个Date类型的,不具有getDate方法的,所以我们需要在get的时候,绑定一下this的指向

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

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