Skip to content

组件的代理对象

在组件使用this可以访问到setupStatepropsattrsslots$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
        },
    }
}

Released under the MIT License.