From 8a298fae5466f8e4873a757a502d3dd737e03971 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 30 Oct 2025 21:34:45 +0800 Subject: [PATCH] feat: add useInstanceOption for safer instance access --- .../src/componentCurrentInstance.ts | 34 +++++++++++++++++++ packages/runtime-core/src/index.ts | 2 +- packages/runtime-dom/src/apiCustomElement.ts | 8 ++--- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/componentCurrentInstance.ts b/packages/runtime-core/src/componentCurrentInstance.ts index 7ac52a2e99..727958fd84 100644 --- a/packages/runtime-core/src/componentCurrentInstance.ts +++ b/packages/runtime-core/src/componentCurrentInstance.ts @@ -5,6 +5,7 @@ import type { } from './component' import { currentRenderingInstance } from './componentRenderContext' import { type EffectScope, setCurrentScope } from '@vue/reactivity' +import { warn } from './warning' /** * @internal @@ -90,3 +91,36 @@ export const setCurrentInstance = ( simpleSetCurrentInstance(instance) } } + +const internalOptions = ['ce'] as const + +/** + * @internal + */ +export const useInstanceOption = ( + key: K, + silent = false, +): { + hasInstance: boolean + value: GenericComponentInstance[K] | undefined +} => { + const instance = getCurrentGenericInstance() + if (!instance) { + if (__DEV__ && !silent) { + warn(`useInstanceOption called without an active component instance.`) + } + return { hasInstance: false, value: undefined } + } + + if (!internalOptions.includes(key)) { + if (__DEV__) { + warn( + `useInstanceOption only accepts ` + + ` ${internalOptions.map(k => `'${k}'`).join(', ')} as key, got '${key}'.`, + ) + } + return { hasInstance: true, value: undefined } + } + + return { hasInstance: true, value: instance[key] } +} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index be0633548c..67ff4f0e91 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -108,7 +108,7 @@ export { getCurrentInstance } from './component' /** * @internal */ -export { getCurrentGenericInstance } from './component' +export { useInstanceOption } from './component' // For raw render function users export { h } from './h' diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index cf95eb4a80..d3f8acfea2 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -27,9 +27,9 @@ import { type VNodeProps, createVNode, defineComponent, - getCurrentGenericInstance, nextTick, unref, + useInstanceOption, warn, } from '@vue/runtime-core' import { @@ -804,12 +804,12 @@ export class VueElement extends VueElementBase< } export function useHost(caller?: string): VueElementBase | null { - const instance = getCurrentGenericInstance() - const el = instance && (instance.ce as VueElementBase) + const { hasInstance, value } = useInstanceOption('ce', true) + const el = value as VueElementBase if (el) { return el } else if (__DEV__) { - if (!instance) { + if (!hasInstance) { warn( `${caller || 'useHost'} called without an active component instance.`, ) -- 2.47.3