Vue 初始化之 _renderProxy
2022/11/10 Vue
...
// 给 _renderProxy 属性赋值,不同环境赋值的结果不同
if (__DEV__) {
initProxy(vm)
} else {
// 属性指向 vue 实例本身
vm._renderProxy = vm
}
...
_init
方法在合并完options
之后,就执行了上面这段代码,在生产环境,_renderProxy指向了Vue实例本身,而非生产环境下则调用了initProxy方法。那么这段代码是做什么的呢?
在 src 里搜索了一下_renderProxy
,发现在src/core/instance/render.ts
里有这么一段代码
// src/core/instance/render.ts
...
vnode = render.call(vm._renderProxy, vm.$createElement)
...
_renderProxy是render函数的执行上下文,在生产环境下是实例本身,而在开发环境时,这个执行上下文则使用initProxy进行了处理。
我们来看看initProxy
是做什么的呢?
let initProxy
// 判断当前环境是否支持 proxy
const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
initProxy = function initProxy(vm) {
// 如果支持 proxy
if (hasProxy) {
// 确定使用哪个 proxy 的 handler
const options = vm.$options
const handlers =
options.render && options.render._withStripped ? getHandler : hasHandler
vm._renderProxy = new Proxy(vm, handlers)
} else {
// 指向 vm 实例
vm._renderProxy = vm
}
}
上来声明了 initProxy,和判断了一下当前环境是否支持 Proxy,Proxy是一个代理,用来拦截对象的各种属性和方法。
如果浏览器不支持Proxy,那Vue 实例的_renderProxy 依旧是vm实例本身。
如果支持Proxy,代码会根据接着会判断 options.render和options.render._withStripped这两项是否配置, 从而来获取 Proxy 的拦截行为 handlers.
- options.render 实例的渲染函数
- options.render._withStripped 是在测试环境的模版编译时会设置此项,让Vue能够使用正确的运行时代理检测
我们来看看这两个handler
const getHandler = {
get(target, key) {
// key是字符串, 并且key 不在target上
if (typeof key === 'string' && !(key in target)) {
if (key in target.$data)
// key 必须是 target.\$data 对象上定义过的
warnReservedPrefix(target, key)
// key没有在实例上定义
else warnNonPresent(target, key)
}
return target[key]
}
}
这个handler 其实是拦截了Vue 实例的get方法,判断一下我们在使用的Vue上的属性,必须是在Vue的$data上定义了的,以及使用不在$data上定义的属性,方便给出报错提示
// 对实例对象的in操作符进行拦截
const hasHandler = {
has(target, key) {
const has = key in target
// 全局的方法
const isAllowed =
allowedGlobals(key) ||
(typeof key === 'string' &&
key.charAt(0) === '_' &&
!(key in target.$data))
// 如果没有在实例上,也不是全局方法
if (!has && !isAllowed) {
if (key in target.$data) warnReservedPrefix(target, key)
else warnNonPresent(target, key)
}
return has || !isAllowed
}
}
所以在开发环境,通过 Proxy 元编程方式对Vue的render函数的执行上下文做了验证拦截处理。在它的getter和in操作符中加入一部分属性的检查,当模板中调用的属性不存在于实例中或不存在于$options中时,及时提出警告。