摘要:不同的代码运行环境赋值的结果不同。当访问的属性不是类型或者属性值在被代理的对象上不存在,则抛出错误提示,否则就返回该属性值。该方法可以在开发者错误的调用属性时,提供提示作用。只不过目前规范还没有很完善,使用的时候要稍加注意。
前几篇文章中,我们主要讲了merge options的一些操作。今天我们回到init方法往下讲。
if (process.env.NODE_ENV !== "production") { initProxy(vm) } else { vm._renderProxy = vm }
上面的代码逻辑很简单,主要就是为Vue实例的_renderProxy属性赋值。不同的代码运行环境赋值的结果不同。
当前环境是开发环境,则调用initProxy方法
如果不是开发环境,则vue实例的_renderProxy属性指向vue实例本身。
initProxy接下来看initProxy方法究竟怎么样代理vm属性呢?
initProxy = function initProxy (vm) { if (hasProxy) { // determine which proxy handler to use const options = vm.$options const handlers = options.render && options.render._withStripped ? getHandler : hasHandler vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
先来看hasProxy的代码
const hasProxy = typeof Proxy !== "undefined" && Proxy.toString().match(/native code/)
从变量的名字我们知道它的作用就是判断当前环境中Proxy是否可用。同学们如果不熟悉Proxy的用法,可以点击这里。这个判断方法在我们平时写代码中也可以进行借鉴。
回到代码中,如果当前环境存在Proxy,则执行块内的语句。
const options = vm.$options const handlers = options.render && options.render._withStripped ? getHandler : hasHandler
上面两行代码的逻辑是,如果options上存在render属性,且render属性上存在_withStripped属性,则proxy的traps(traps其实也就是自定义方法)采用getHandler方法,否则采用hasHandler方法。更多关于traps相关的内容可点击这里。
接下来看看getHandler和hasHandler方法
const getHandler = { get (target, key) { if (typeof key === "string" && !(key in target)) { warnNonPresent(target, key) } return target[key] } }
getHandler方法主要是针对读取代理对象的某个属性时进行的操作。当访问的属性不是string类型或者属性值在被代理的对象上不存在,则抛出错误提示,否则就返回该属性值。
该方法可以在开发者错误的调用vm属性时,提供提示作用。
const hasHandler = { has (target, key) { const has = key in target const isAllowed = allowedGlobals(key) || key.charAt(0) === "_" if (!has && !isAllowed) { warnNonPresent(target, key) } return has || !isAllowed } }
hasHandler方法的应用场景在于查看vm实例是否拥有某个属性。比如调用for in循环遍历vm实例属性时,会触发hasHandler方法。
首先使用in操作符判断该属性是否在vm实例上存在。
const has = key in target
接下来通过下列语句,看属性名称是否可用?
const isAllowed = allowedGlobals(key) || key.charAt(0) === "_"
allowedGlobals的定义如下
const allowedGlobals = makeMap( "Infinity,undefined,NaN,isFinite,isNaN," + "parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent," + "Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl," + "require" // for Webpack/Browserify )
我们结合makeMap函数一起来看
export function makeMap ( str: string, expectsLowerCase?: boolean ): (key: string) => true | void { const map = Object.create(null) const list: Array= str.split(",") for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] }
先来分析makeMap,其作用是通过传入的string参数来生成映射表。如传入下列参数
"Infinity,undefined,NaN,isFinite,isNaN," ....
通过makeMap方法可以生成下面这样的一个映射表
{ Infinity: true, undefined: true ...... }
这个方法非常有用,我们在日常写代码的时候也可以进行借鉴。
回到hasHandler源码中。allowedGlobals最终存储的是一个代表特殊属性名称的映射表。
所以结合has和isAllowed属性,我们知道当读取对象属性时,如果属性名在vm上不存在,且不在特殊属性名称映射表中,或没有以_符号开头。则抛出异常。
最后回到initProxy代码中
if (hasProxy) { ... vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
如果Proxy属性存在,则把包装后的vm属性赋值给_renderProxy属性值。否则把vm是实例本身赋值给_renderProxy属性
总结The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
代理对象是es6的新特性,它主要用来自定义对象一些基本操作(如查找,赋值,枚举等)。
我们上面讲的这些代码主要应用了lookup和enumeration部分的自定义能力。proxy是一个强大的特性,为我们提供了很多"元编程"能力。只不过目前规范还没有很完善,使用的时候要稍加注意。最后按照我们的老规矩,以一张图完成今天的讲解。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94916.html
摘要:果然我们找到了的构造函数定义。告诉你是一个构造函数,需要用操作符去调用。在深入方法之前,我们先把目光移到文件里在的构造函数定义之后,有一系列方法会被立即调用。下篇博文主要介绍相关的内容,涉及到原型链和构造函数以及部分的实现,敬请期待 上篇博文中说到了Vue源码的目录结构是什么样的,每个目录的作用我们应该也有所了解。我们知道core/instance目录主要是用来实例化Vue对象,所以我...
摘要:阅读的源码,或者说阅读一个框架的源码,了解它的目录结构都是很有帮助的。人人都能懂的源码系列文章将会详细的介绍源码的方方面面。 阅读Vue的源码,或者说阅读一个框架的源码,了解它的目录结构都是很有帮助的。下面我们来看看Vue源码的目录结构。showImg(https://segmentfault.com/img/bV8fLS?w=598&h=654); Vue各目录简介 下图是Vue各个...
摘要:主要是通过为我们属性添加一些自定义的行为。方法用来初始化一些生命周期相关的属性,以及为等属性赋值,来看源码。名称说明指定已创建的实例之父实例,在两者之间建立父子关系。一个对象,持有已注册过的所有子组件。 上篇文章,我们讲了vm._renderProxy相关的内容。主要是通过Proxy为我们vm属性添加一些自定义的行为。今天我们回到init方法中,为大家讲解initLifecycle。i...
摘要:上一篇文章中说道,函数要分两种情况进行说明,第一种是为基础构造器的情况,这个已经向大家介绍过了,今天这篇文章主要介绍第二种情况,是创建的子类。表示的是当前构造器上新增的,表示的是当前构造器上新增的封装。 上一篇文章中说道,resolveConstructorOptions函数要分两种情况进行说明,第一种是Ctor为基础构造器的情况,这个已经向大家介绍过了,今天这篇文章主要介绍第二种情况...
摘要:上一篇文章中说道,函数要分两种情况进行说明,第一种是为基础构造器的情况,这个已经向大家介绍过了,今天这篇文章主要介绍第二种情况,是创建的子类。表示的是当前构造器上新增的,表示的是当前构造器上新增的封装。 上一篇文章中说道,resolveConstructorOptions函数要分两种情况进行说明,第一种是Ctor为基础构造器的情况,这个已经向大家介绍过了,今天这篇文章主要介绍第二种情况...
阅读 648·2021-11-11 16:55
阅读 2159·2021-11-11 16:55
阅读 1950·2021-11-11 16:55
阅读 2340·2021-10-25 09:46
阅读 1597·2021-09-22 15:20
阅读 2267·2021-09-10 10:51
阅读 1702·2021-08-25 09:38
阅读 2612·2019-08-30 12:48