Appearance
组件的代理对象
在组件使用this可以访问到setupState、props、attrs、slots、$refs等属性,这些属性都是通过代理对象实现的。
组件的代理对象实现:
ts
// component.ts
/**
* 初始化组件
*/
export function setupComponent(instance) {
/**
* 初始化属性
*/
initProps(instance)
setupStateFulComponent(instance)
}
const publicPropertiesMap = {
$attrs: instance => instance.attrs,
$slots: instance => instance.slots,
$refs: instance => instance.refs,
$nextTick: instance => {
// TODO
},
}
const publicInstanceProxyHandlers = {
get(target, key) {
const { _: instance } = target
const { setupState, props } = instance
/**
* 如果访问了某个属性,先去setupState里面找,
* 如果没有,在去props里面找
*/
// 在setupState里面找
if (hasOwn(setupState, key)) {
return setupState[key]
}
if (hasOwn(props, key)) {
return props[key]
}
/**
* 访问了, $attrs $slots $refs
*/
if (hasOwn(publicPropertiesMap, key)) {
const publicGetter = publicPropertiesMap[key]
return publicGetter(instance)
}
/**
* 如果实在没有
*/
return instance[key]
},
set(target, key, value) {
const { _: instance } = target
const { setupState } = instance
if (hasOwn(setupState, key)) {
/**
* 修改setupState
*/
setupState[key] = value
}
return true
},
}
function setupStateFulComponent(instance) {
const { type } = instance
/**
* 创建代理对象,内部访问setupState,props,$attrs, $slots这些
*/
instance.proxy = new Proxy(instance.ctx, publicInstanceProxyHandlers)
if (isFunction(type.setup)) {
const setupContext = createSetupContext(instance)
// 保存setupContext
instance.setupContext = setupContext
const setupResult = type.setup(instance.props, setupContext)
handlerSetupResult(instance, setupResult)
}
if (!instance.render) {
// 如果上面处理完了,instance还是没有render,那就从组件里面获取
// 将render函数给instance
instance.render = type.render
}
}
function handlerSetupResult(instance, setupResult) {
if (isFunction(setupResult)) {
// 如果setup返回了函数,就认定是render
instance.render = setupResult
} else if (isObject(setupResult)) {
// 拿到setup返回的状态
// 如果返回了对象,就是状态
instance.setupState = proxyRefs(setupResult)
}
}
/**
* 创建setupContext
*/
function createSetupContext(instance) {
return {
get attrs() {
return instance.attrs
},
}
}