From e34276315462caa66ebb6528961fc63d854a0ea0 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 20 Apr 2024 01:57:42 +0800 Subject: [PATCH 1/7] feat(runtime-vapor): add expose related api --- packages/runtime-vapor/src/component.ts | 37 ++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index e33233e10..cf60f2204 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,5 +1,5 @@ -import { EffectScope } from '@vue/reactivity' -import { EMPTY_OBJ, NOOP, isFunction } from '@vue/shared' +import { EffectScope, isRef } from '@vue/reactivity' +import { EMPTY_OBJ, isArray, isFunction } from '@vue/shared' import type { Block } from './apiRender' import type { DirectiveBinding } from './directives' import { @@ -45,6 +45,30 @@ export type SetupContext = E extends any 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) @@ -58,7 +82,7 @@ export function createSetupContext( get emit() { return (event: string, ...args: any[]) => instance.emit(event, ...args) }, - expose: NOOP, + expose, }) } else { return { @@ -67,7 +91,7 @@ export function createSetupContext( }, emit: instance.emit, slots: instance.slots, - expose: NOOP, + expose, } } } @@ -114,9 +138,12 @@ export interface ComponentInternalInstance { attrs: Data slots: InternalSlots refs: Data + // exposed properties via expose() + exposed: Record | null attrsProxy?: Data slotsProxy?: Slots + exposeProxy: Record | null // lifecycle isMounted: boolean @@ -238,6 +265,8 @@ export function createComponentInstance( attrs: EMPTY_OBJ, slots: EMPTY_OBJ, refs: EMPTY_OBJ, + exposed: null, + exposeProxy: null, // lifecycle isMounted: false, From 888bed941fd9d2db367e592ccdfd9cecf3d19cfc Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 20 Apr 2024 18:50:51 +0800 Subject: [PATCH 2/7] feat(runtimer-vapor): add test case --- .../__tests__/componentExpose.spec.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 packages/runtime-vapor/__tests__/componentExpose.spec.ts diff --git a/packages/runtime-vapor/__tests__/componentExpose.spec.ts b/packages/runtime-vapor/__tests__/componentExpose.spec.ts new file mode 100644 index 000000000..7e046f9bc --- /dev/null +++ b/packages/runtime-vapor/__tests__/componentExpose.spec.ts @@ -0,0 +1,49 @@ +import { describe, expect } from 'vitest' +import { makeRender } from './_utils' +import { type Ref, ref } from '@vue/reactivity' + +const define = makeRender() + +describe('component expose', () => { + test('should work', async () => { + const expxosedObj = { foo: 1 } + const { render } = define({ + setup(_, { expose }) { + expose(expxosedObj) + }, + }) + const { instance } = render() + expect(instance.exposed).toEqual(expxosedObj) + }) + + test('should warn when called multiple times', async () => { + const { render } = define({ + setup(_, { expose }) { + expose() + expose() + }, + }) + render() + expect( + 'expose() should be called only once per setup().', + ).toHaveBeenWarned() + }) + + test('should warn when passed non-object', async () => { + const exposedRef = ref([1, 2, 3]) + const { render } = define({ + setup(_, { expose }) { + expose(exposedRef.value) + }, + }) + render() + expect( + 'expose() should be passed a plain object, received array.', + ).toHaveBeenWarned() + exposedRef.value = ref(1) + render() + expect( + 'expose() should be passed a plain object, received ref.', + ).toHaveBeenWarned() + }) +}) From b6df8ae23a12262a3108a656c0314cf734ebe4b4 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 20 Apr 2024 18:56:11 +0800 Subject: [PATCH 3/7] feat(runtime-vapor): add getExposeProxy --- packages/runtime-vapor/src/component.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index cf60f2204..8c16003d6 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,4 +1,4 @@ -import { EffectScope, isRef } from '@vue/reactivity' +import { EffectScope, isRef, markRaw, proxyRefs } from '@vue/reactivity' import { EMPTY_OBJ, isArray, isFunction } from '@vue/shared' import type { Block } from './apiRender' import type { DirectiveBinding } from './directives' @@ -370,3 +370,21 @@ function getSlotsProxy(instance: ComponentInternalInstance): Slots { })) ) } + +export function getExposeProxy(instance: ComponentInternalInstance) { + if (instance.exposed) { + return ( + instance.exposeProxy || + (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), { + get(target, key: string) { + if (key in target) { + return target[key] + } + }, + has(target, key: string) { + return key in target + }, + })) + ) + } +} From 77b44a7ec244bb5f8d39682f191e8926c4bb1b3f Mon Sep 17 00:00:00 2001 From: Doctor Wu <44631608+Doctor-wu@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:08:27 +0800 Subject: [PATCH 4/7] Update packages/runtime-vapor/src/component.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kevin Deng 三咲智子 --- packages/runtime-vapor/src/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 8c16003d6..5d0be1aca 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -143,7 +143,7 @@ export interface ComponentInternalInstance { attrsProxy?: Data slotsProxy?: Slots - exposeProxy: Record | null + exposeProxy?: Record // lifecycle isMounted: boolean From 5796cfe91f7b419818f37fd66c52fc3b44f48860 Mon Sep 17 00:00:00 2001 From: Doctor Wu <44631608+Doctor-wu@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:08:37 +0800 Subject: [PATCH 5/7] Update packages/runtime-vapor/src/component.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kevin Deng 三咲智子 --- packages/runtime-vapor/src/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 5d0be1aca..aed92fa02 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -139,7 +139,7 @@ export interface ComponentInternalInstance { slots: InternalSlots refs: Data // exposed properties via expose() - exposed: Record | null + exposed?: Record attrsProxy?: Data slotsProxy?: Slots From 1db4121e6a9a378b492dbf645189276f05705ccc Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 20 Apr 2024 22:10:36 +0800 Subject: [PATCH 6/7] feat(runtime-vapor): simplify impl --- packages/runtime-vapor/src/component.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index aed92fa02..f03c0cde5 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -265,8 +265,6 @@ export function createComponentInstance( attrs: EMPTY_OBJ, slots: EMPTY_OBJ, refs: EMPTY_OBJ, - exposed: null, - exposeProxy: null, // lifecycle isMounted: false, @@ -370,21 +368,3 @@ function getSlotsProxy(instance: ComponentInternalInstance): Slots { })) ) } - -export function getExposeProxy(instance: ComponentInternalInstance) { - if (instance.exposed) { - return ( - instance.exposeProxy || - (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), { - get(target, key: string) { - if (key in target) { - return target[key] - } - }, - has(target, key: string) { - return key in target - }, - })) - ) - } -} From 2a5872c9830a3897a3bc3c1fa9dce3ebb10ec2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sat, 20 Apr 2024 22:14:08 +0800 Subject: [PATCH 7/7] chore: remove unused --- packages/runtime-vapor/src/component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index f03c0cde5..43e89aaa5 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,4 +1,4 @@ -import { EffectScope, isRef, markRaw, proxyRefs } from '@vue/reactivity' +import { EffectScope, isRef } from '@vue/reactivity' import { EMPTY_OBJ, isArray, isFunction } from '@vue/shared' import type { Block } from './apiRender' import type { DirectiveBinding } from './directives'