From 19813db08712a2d7a0d70a921bedc34b0bae05ae Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Tue, 19 Dec 2023 16:36:05 +0800 Subject: [PATCH 1/2] feat: Added `expose` to component instances --- packages/runtime-core/src/index.ts | 2 +- packages/runtime-vapor/src/component.ts | 61 ++++++++++++++++++++++++- packages/runtime-vapor/src/directive.ts | 7 ++- packages/runtime-vapor/src/render.ts | 3 +- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 9770e6ba0..6af3ca430 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -90,7 +90,7 @@ export { // For getting a hold of the internal instance in setup() - useful for advanced // plugins -export { getCurrentInstance } from './component' +export { getCurrentInstance, getExposeProxy } from './component' // For raw render function users export { h } from './h' diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 23b5f0770..443e3b925 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,6 +1,6 @@ -import { EffectScope, Ref, ref } from '@vue/reactivity' +import { EffectScope, isRef, Ref, ref } from '@vue/reactivity' -import { EMPTY_OBJ } from '@vue/shared' +import { EMPTY_OBJ, isArray } from '@vue/shared' import { Block } from './render' import { type DirectiveBinding } from './directive' import { @@ -11,6 +11,7 @@ import { import type { Data } from '@vue/shared' import { VaporLifecycleHooks } from './apiLifecycle' +import { warn } from '@vue/runtime-core' export type Component = FunctionalComponent | ObjectComponent @@ -42,6 +43,10 @@ export interface ComponentInternalInstance { props: Data setupState: Data + // exposed properties via expose() + exposed: Record | null + exposeProxy: Record | null + /** directives */ dirs: Map @@ -123,6 +128,54 @@ export const unsetCurrentInstance = () => { currentInstance = null } +// TODO: Maybe it can be reused +// TODO: https://github.com/vuejs/core/blob/04d2c05054c26b02fbc1d84839b0ed5cd36455b6/packages/runtime-core/src/component.ts#L186C1-L186C1 +export type SetupContext = { + expose: (exposed?: Record) => void +} + +// TODO: Maybe it can be reused +// TODO: https://github.com/vuejs/core/blob/04d2c05054c26b02fbc1d84839b0ed5cd36455b6/packages/runtime-core/src/component.ts#L983 +export function createSetupContext( + instance: ComponentInternalInstance, +): SetupContext { + const expose: SetupContext['expose'] = (exposed) => { + if (__DEV__) { + if (instance.exposed) { + warn(`expose() should be called only once per setup().`) + } + if (exposed != null) { + let exposedType: string = typeof exposed + if (exposedType === 'object') { + if (isArray(exposed)) { + exposedType = 'array' + } else if (isRef(exposed)) { + exposedType = 'ref' + } + } + if (exposedType !== 'object') { + warn( + `expose() should be passed a plain object, received ${exposedType}.`, + ) + } + } + } + instance.exposed = exposed || {} + } + + if (__DEV__) { + // We use getters in dev in case libs like test-utils overwrite instance + // properties (overwrites should not be done in prod) + return Object.freeze({ + expose, + }) + } else { + return { + expose, + } + } +} + let uid = 0 export const createComponentInstance = ( component: ObjectComponent | FunctionalComponent, @@ -145,6 +198,10 @@ export const createComponentInstance = ( props: EMPTY_OBJ, setupState: EMPTY_OBJ, + // exposed properties via expose() + exposed: null, + exposeProxy: null, + dirs: new Map(), // lifecycle diff --git a/packages/runtime-vapor/src/directive.ts b/packages/runtime-vapor/src/directive.ts index 3906eb723..15267623e 100644 --- a/packages/runtime-vapor/src/directive.ts +++ b/packages/runtime-vapor/src/directive.ts @@ -1,6 +1,7 @@ import { isFunction } from '@vue/shared' import { currentInstance, type ComponentInternalInstance } from './component' import { effect } from './scheduler' +import { ComponentPublicInstance, getExposeProxy } from '@vue/runtime-core' export type DirectiveModifiers = Record @@ -69,6 +70,10 @@ export function withDirectives( } const instance = currentInstance + // TODO: Wait for the component instance type to be completed before deleting `any` + const instanceProxy = + (getExposeProxy(currentInstance as any) as ComponentInternalInstance) || + currentInstance.proxy if (!instance.dirs.has(node)) instance.dirs.set(node, []) const bindings = instance.dirs.get(node)! @@ -84,7 +89,7 @@ export function withDirectives( const binding: DirectiveBinding = { dir, - instance, + instance: instanceProxy, source, value: null, // set later oldValue: null, diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index a2f505ecd..4e3519415 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -4,6 +4,7 @@ import { type Component, type ComponentInternalInstance, createComponentInstance, + createSetupContext, setCurrentInstance, unsetCurrentInstance, } from './component' @@ -42,7 +43,7 @@ export function mountComponent( setCurrentInstance(instance) const block = instance.scope.run(() => { const { component, props } = instance - const ctx = { expose: () => {} } + const ctx = createSetupContext(instance) const setupFn = typeof component === 'function' ? component : component.setup From 999cdf207da72869e4567fb3445b84cffbf58375 Mon Sep 17 00:00:00 2001 From: baiwusanyu-c <740132583@qq.com> Date: Tue, 19 Dec 2023 16:47:57 +0800 Subject: [PATCH 2/2] chore: format code --- packages/runtime-vapor/src/directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/directive.ts b/packages/runtime-vapor/src/directive.ts index 15267623e..916e6e101 100644 --- a/packages/runtime-vapor/src/directive.ts +++ b/packages/runtime-vapor/src/directive.ts @@ -1,7 +1,7 @@ import { isFunction } from '@vue/shared' import { currentInstance, type ComponentInternalInstance } from './component' import { effect } from './scheduler' -import { ComponentPublicInstance, getExposeProxy } from '@vue/runtime-core' +import { getExposeProxy } from '@vue/runtime-core' export type DirectiveModifiers = Record