vue的寂寞:_renderProxy属性在vue初始化函数_init()函数中,有这么一段代码:if(process.env.NODE_ENV!=='production'){initProxy(vm)}else{vm._renderProxy=vm}这段代码的目的是给Vue实例的_renderProxy属性赋值,这个_renderProxy在可视化的render函数中使用。我们在vue/src/core/instance/render.js中找到这段代码:const{render,_parentVnode}=vm.$options...currentRenderingInstance=vmvnode=render.call(vm._renderProxy,vm.$createElement)当我们创建一个Vue根实例,我们通常会传入一个render函数:newVue({el:'#app',render:h=>h(App),router});因此,这个vm._renderProxy实际上指定了我们传入的render函数在创建Vnode时执行的上下文this。回到上面,这个initProxy函数是如何给_renderProxy属性赋值的呢?我们来看看具体的代码:initProxy=functioninitProxy(vm){if(hasProxy){//判断使用哪个proxyhandlerconstoptions=vm.$optionsconsthandlers=options.render&&options.render._withStripped?getHandler:hasHandlervm._renderProxy=newProxy(vm,handlers)}else{vm._renderProxy=vm}}所以我们的_renderProxy属性赋值可以概括为:当前环境为开发环境,满足hasProxy条件,然后调用Proxy方法,为vue实例添加代理,否则vue实例的_renderProxy属性指向vue实例本身。要代理寂寞吗:hasProxyexportfunctionisNative(Ctor:any):boolean{returntypeofCtor==='function'&&/nativecode/.test(Ctor.toString())}consthasProxy=typeofProxy!=='undefined'&&isNative(Proxy)结合vue/src/core/util/env.js中的isNative函数我们知道,hasProxy函数就是它的字面意思:Proxy在当前环境下是否可用。也就是说当前环境是开发环境,如果有Proxy,则调用Proxy方法为vue实例添加代理。getHandler和hasHandler:两个孤独的爆发Proxy的处理对象是一个占位符对象,包含了Proxy的陷阱(Trap)函数。从上面的代码我们可以知道我们在代理vue实例的时候使用了两个Trap函数:当vue实例中存在options.render,并且options.render._withStripped为true时,我们使用getHandler函数,即handler.get()代理实例,在读取代理对象的属性时触发操作,例如执行proxy.foo时。其他情况使用hasHandler,即handler.has()代理实例,在判断代理对象是否具有某个属性时触发操作,比如在proxy中执行“foo”时。constgetHandler={get(target,key){if(typeofkey==='string'&&!(keyintarget)){if(keyintarget.$data)warnReservedPrefix(target,key)elsewarnNonPresent(target,key)}returntarget[key]}}第一种策略,当我们读取vm实例的一个属性时,如果不是string类型或者该属性值在vm实例上不存在,就会抛出错误信息。当然,有两种类型的错误。如果在$data上找到该属性,则会报错:constwarnReservedPrefix=(target,key)=>{warn(`Property"${key}"mustbeaccessedwith"$data.${key}"因为`+'以“$”或“_”开头的属性不会在Vue实例中代理,以'+'防止与Vue内部发生冲突。'+'参见:https://vuejs.org/v2/api/#data',target)}从错误报告中可以知道原因。如果我们在严格模式下代理以$或_开头的属性,我们必须通过$data.${key}的方式来区别于vue的内部方法。其他情况下,当属性不存在时,会报如下警告,constwarnNonPresent=(target,key)=>{warn(`Propertyormethod"${key}"isnotdefinedontheinstancebut`+'在渲染期间引用。确保此属性是反应性的,'+'在数据选项中,或对于基于类的组件,通过'+'初始化属性。'+'参见:https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',target)}这个警告是不是特别眼熟?在开发环境中,如果忘记给数据或方法添加属性或方法,就会经常看到这个警告。第二种策略,当我们检查vm实例是否有某个属性时,比如调用forin循环遍历vm实例属性,会触发hasHandler方法consthasHandler={has(target,key){consthas=keyin目标constisAllowed=allowedGlobals(key)||key.charAt(0)==='_'if(!has&&!isAllowed){warnNonPresent(target,key)}返回有||!isAllowed}}读取vm对象属性时,如果属性名在vm实例上不存在,且不在特殊属性名映射表中,或者不以_符号开头。然后抛出上面不存在的警告。总结:用测试用例排解孤独感。上面提到了很多源码,比较抽象,没有场景落地。这真的很难理解。这里我们使用vue测试用例vue/test/unit/features/instance/render-proxy.spec.js来说明以上两种策略:it('shouldwarnmissingpropertyinrenderfnswith`with`',()=>{newVue({template:`
