From 83eb7bdfa22a240ca3a3edf0342b80d48ae63f81 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 3 Dec 2025 17:01:19 +0800 Subject: [PATCH] fix(runtime-vapor): add dev-only warning for non-existent property access during render --- .../runtime-vapor/__tests__/component.spec.ts | 16 ++++++++++ packages/runtime-vapor/__tests__/hmr.spec.ts | 2 +- packages/runtime-vapor/src/component.ts | 32 +++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/runtime-vapor/__tests__/component.spec.ts b/packages/runtime-vapor/__tests__/component.spec.ts index ce901e19931..84643614983 100644 --- a/packages/runtime-vapor/__tests__/component.spec.ts +++ b/packages/runtime-vapor/__tests__/component.spec.ts @@ -416,6 +416,22 @@ describe('component', () => { 'Vapor component setup() returned non-block value, and has no render function', ).toHaveBeenWarned() }) + + it('warn non-existent property access', () => { + define({ + setup() { + return {} + }, + render(ctx: any) { + ctx.foo + return [] + }, + }).render() + + expect( + 'Property "foo" was accessed during render but is not defined on instance.', + ).toHaveBeenWarned() + }) }) function getEffectsCount(scope: EffectScope): number { diff --git a/packages/runtime-vapor/__tests__/hmr.spec.ts b/packages/runtime-vapor/__tests__/hmr.spec.ts index 23cb2049803..5bee42ecd73 100644 --- a/packages/runtime-vapor/__tests__/hmr.spec.ts +++ b/packages/runtime-vapor/__tests__/hmr.spec.ts @@ -631,7 +631,7 @@ describe('hot module replacement', () => { }, render: compileToFunction(` -
+
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index bc72fcf7cde..19b608d82f2 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -45,11 +45,13 @@ import { onScopeDispose, proxyRefs, setActiveSub, + toRaw, unref, } from '@vue/reactivity' import { EMPTY_OBJ, ShapeFlags, + hasOwn, invokeArrayFns, isArray, isFunction, @@ -381,9 +383,10 @@ export function setupComponent( ) instance.block = [] } else { - instance.devtoolsRawSetupState = setupResult - // TODO make the proxy warn non-existent property access during dev instance.setupState = proxyRefs(setupResult) + if (__DEV__) { + instance.setupState = createDevSetupStateProxy(instance) + } devRender(instance) } } else { @@ -449,6 +452,31 @@ export function applyFallthroughProps( isApplyingFallthroughProps = false } +/** + * dev only + */ +function createDevSetupStateProxy( + instance: VaporComponentInstance, +): Record { + const { setupState } = instance + return new Proxy(setupState!, { + get(target, key: string | symbol, receiver) { + if ( + isString(key) && + !key.startsWith('__v') && + !hasOwn(toRaw(setupState)!, key) + ) { + warn( + `Property ${JSON.stringify(key)} was accessed during render ` + + `but is not defined on instance.`, + ) + } + + return Reflect.get(target, key, receiver) + }, + }) +} + /** * dev only */