渲染函数的作用域添加代理
if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm }
上面这段代码可以看到在非生产环境下执行了initProxy
函数,参数是实例;在生产环境下设置了实例的_renderProxy
属性为实例自身,那么先来看看 initProxy
函数,这个函数在core/instance/proxy.js
文件下。
initProxy = function initProxy (vm) { //判断是否支持ES6的Proxy或存在Proxy函数 const hasProxy =typeof Proxy !== 'undefined' && isNative(Proxy) ... //支持Proxy if (hasProxy) { // determine which proxy handler to use //缓存合并选项后的vm.$options const options = vm.$options //_withStripped这个属性只在测试代码中出现过所以一般为false,也就是赋值为hasHandler const handlers = options.render && options.render._withStripped ? getHandler : hasHandler //为vm实例添加代理,对以下情况时进行拦截 /* 1、属性查询: foo in proxy 2、继承属性查询: foo in Object.create(proxy) 3、with 检查: with(proxy) { (foo); } 4、Reflect.has() */ vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
可以看到开头就是一个判断变量hasProxy
,其作用是确定该环境是否支持Proxy
,当支持情况下继续执行下面的代码: 读取实例的$options
属性, 最后为实例添加_renderProxy
属性,这样就和生产环境一致了。只不过对当读取_renderProxy
的属性时进行了拦截操作,当该属性存在render
函数和 render._withStripped
属性时,执行的是getHandler
拦截函数;否则就是执行hasHandler
拦截函数,那么现在来看看这两个拦截函数都干了什么?
hasHandler
// 判断传入的参数是否存在于下面这段字符中 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 ) //比如: in运算符的拦截,但对for...in循环不生效 const hasHandler = { //拦截key in target的操作,返回一个布尔值 has (target, key) { const has = key in target //是否存在与目标中 //判断key名是否存在于allowedGlobals函数中生成的对象中 || (key名是string类型 && 首字母是_) const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_') //访问了一个没有定义在实例对象上(或原型链上)的属性 && isAllowed为false提示警告 if (!has && !isAllowed) { warnNonPresent(target, key) } //返回一个布尔值 return has || !isAllowed } }
该函数的作用就是判断对象是否具有某个属性。可以看到首先用in
操作符判断这个属性是否存在于_renderProxy
中,当不存在 && (属性名不为字符串 || 属性名首字母为_) && allowedGlobals(key)
为false
时,提示警告信息'属性或方法没有在实例上定义'
getHandler
//添加读取属性拦截 const getHandler = { get (target, key) { //key名是字符串类型 && key不存在于目标中提示警告 if (typeof key === 'string' && !(key in target)) { warnNonPresent(target, key) } return target[key] } }
这个比较好理解,就是读取_renderProxy
中的属性的时候,当属性名为字符串 && 不存在_renderProxy
中时提示警告信息'属性或方法没有在实例上定义'。
一个实例的生成后的一些隐藏所有属性
vm = { _renderProxy: vm, _self: vm, $options: { _parentListeners: {},//所有父组件作用于该组件的监听函数集合 },//用户可配置的属性和一些不可配置的属性 $parent,//实例的父组件 $children: [],//子组件集合 $root,//根组件一般为App $refs:{},//存放该组件中所有存在`ref`属性的节点 _watcher,//该实例渲染函数的数据的订阅者 _inactive,//表示该组件是否激活, `false`代表激活 `true`或`null`代表未激活 _directInactive,//表示`keep-alive`中组件状态的属性,表示是否要激活 比如父组件未激活 那么其也不会激活 _isMounted,//用于表示该组件是否已挂载 _isDestroyed,//表示该组件是否已销毁 _isBeingDestroyed,//表示该组件是否正在销毁中 _events,//组件的所有监听函数集合 _hasHookEvent,//父组件是否有监听子组件的生命周期钩子 }
这一章节到这就没了,下一章我们来看下‘初始化数据’做了什么
详细信息可访问https://github.com/maomao93/vue-2.x/tree/master/personal