最近研究了vue3的源码,知道使用了Proxy和Reflect,但是没搞明白它们之间的关系本文主要是让大家理解为什么vue3要使用Proxy和Reflect和responsive部分为什么使用Proxy(ProxyandObject.defineproperty)Object.defineproperty实现对象监听的原理首先分析vue2中使用的Object.definepropertyletobj={a:10}Object.keys(obj).forEach(key=>{letvalue=obj[key]Object.defineProperty(obj,key,{set(newValue){console.log(`监听${key}变化:${newValue}`);value=newValue},get(){console.log(`获取${key}对应的值:${value}`);返回值}})})obj.a=100//监听一个变化:100obj.b=10//将不被监听通过在上面的例子中,我们可以看到obj新添加的属性b在vue2中是不会被监听和使用的。我们也会遇到这样的问题#template
{{obj.a}}
{{obj.b}}
#srcriptdata(){return{obj:{a:1}}},mounted(){this.obj.b=1;},方法:{addb(item){item.b+=1;控制台日志(this.obj.b)},adda(item){item.a+=1;}}我们发现点击obj.a是有响应的,页面会更新,新添加的obj.b点击不会被vue2使用Object.defineproperty无法监听新添加的对象属性。针对这个问题,vue2提供了$set方法来解决。mounted(){this.$set(this.obj,"b",1)}Proxy实现对象监控letobj={a:10}consthandler={get(target,prop){console.log(`获取${prop}对应的值:${target[prop]}`);返回目标[道具];},set(target,prop,val){target[prop]=val;console.log(`监听${prop}变化:${val}`);returntrue}}letobj2=newProxy(obj,handler)obj2.b=100//Monitorbchange:100我们可以看到通过Proxy实例可以监听到新增的属性。当然Proxy还可以做很多其他的功能,这里就不多介绍了。对Reflect中Reflect的使用感到不解。为什么要用Reflect.get和Reflect.set。搜索了一些文章,对Reflect有了大概的了解。我会用一些问题来说明Proxy在Reflect中的用处。我们有一个具有对象的_name属性的用户和一个getter这是围绕它的代理:letuser={_name:"Guest",getname(){returnthis._name;}};让userProxy=newProxy(user,{get(target,prop,receiver){returntarget[prop];}});console.log(userProxy.name);//Guest这对于我们的例子来说已经足够了。一切似乎都很好。但是让我们让这个例子更复杂。从user继承另一个对象admin后,我们可以观察到不正确的行为:}};letuserProxy=newProxy(user,{get(target,prop,receiver){console.log(target)//用户对象{_name:"Guest"}returntarget[prop];}});letadmin={__proto__:userProxy,_name:"Admin"};console.log(admin.name);//Guest读取admin.name应该返回“Admin”,而不是“Guest”!怎么了?也许我们在继承方面做错了什么?问题实际上出在代理所在的行:当我们读取admin.name时,由于admin对象没有自己的属性,因此搜索转到其原型。原型是当userProxyname从代理读取一个属性时,它的get触发并从原始对象返回该属性,它在this=target的上下文中运行它的代码。因此,结果this._name来自原始对象目标,即:来自用户。这就是Reflect.get派上用场的地方。如果我们使用它,一切都会正常工作。让user={_name:"Guest",getname(){returnthis._name;}};letuserProxy=newProxy(user,{get(target,prop,receiver){//receiver=adminreturnReflect.get(target,prop,receiver);}});letadmin={__proto__:userProxy,_name:"管理员"};控制台日志(管理员名称);//AdminReflect.get中的receiver参数保留了正确的引用Areferencetothis(ieadmin)将正确的对象用法从Reflect.get传递给get。相关文章https://javascript.info/proxy