fix(runtime-vapor): fix readonly warning when useTemplateRef has same variable name as template ref

close #13665
This commit is contained in:
daiwei 2025-07-21 11:54:33 +08:00
parent 56a7f9dd18
commit 7b1b7cfb7f
3 changed files with 39 additions and 24 deletions

View File

@ -562,3 +562,7 @@ export { initFeatureFlags } from './featureFlags'
* @internal * @internal
*/ */
export { createInternalObject } from './internalObject' export { createInternalObject } from './internalObject'
/**
* @internal
*/
export { validateTemplateRef } from './rendererTemplateRef'

View File

@ -14,7 +14,11 @@ import { warn } from './warning'
import { isRef, toRaw } from '@vue/reactivity' import { isRef, toRaw } from '@vue/reactivity'
import { ErrorCodes, callWithErrorHandling } from './errorHandling' import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { queuePostRenderEffect } from './renderer' import { queuePostRenderEffect } from './renderer'
import { type ComponentOptions, getComponentPublicInstance } from './component' import {
type ComponentOptions,
type Data,
getComponentPublicInstance,
} from './component'
import { knownTemplateRefs } from './helpers/useTemplateRef' import { knownTemplateRefs } from './helpers/useTemplateRef'
/** /**
@ -73,25 +77,7 @@ export function setRef(
const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
const setupState = owner.setupState const setupState = owner.setupState
const rawSetupState = toRaw(setupState) const canSetSetupRef = validateTemplateRef(setupState)
const canSetSetupRef =
setupState === EMPTY_OBJ
? () => false
: (key: string) => {
if (__DEV__) {
if (hasOwn(rawSetupState, key) && !isRef(rawSetupState[key])) {
warn(
`Template ref "${key}" used on a non-ref value. ` +
`It will not work in the production build.`,
)
}
if (knownTemplateRefs.has(rawSetupState[key] as any)) {
return false
}
}
return hasOwn(rawSetupState, key)
}
// dynamic ref changed. unset old ref // dynamic ref changed. unset old ref
if (oldRef != null && oldRef !== ref) { if (oldRef != null && oldRef !== ref) {
@ -161,3 +147,26 @@ export function setRef(
} }
} }
} }
export function validateTemplateRef(
setupState: Data,
): (key: string) => boolean {
const rawSetupState = toRaw(setupState)
return setupState === EMPTY_OBJ
? () => false
: (key: string) => {
if (__DEV__) {
if (hasOwn(rawSetupState, key) && !isRef(rawSetupState[key])) {
warn(
`Template ref "${key}" used on a non-ref value. ` +
`It will not work in the production build.`,
)
}
if (knownTemplateRefs.has(rawSetupState[key] as any)) {
return false
}
}
return hasOwn(rawSetupState, key)
}
}

View File

@ -10,6 +10,7 @@ import {
type SchedulerJob, type SchedulerJob,
callWithErrorHandling, callWithErrorHandling,
queuePostFlushCb, queuePostFlushCb,
validateTemplateRef,
warn, warn,
} from '@vue/runtime-dom' } from '@vue/runtime-dom'
import { import {
@ -55,6 +56,7 @@ export function setRef(
const refs = const refs =
instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs
const canSetSetupRef = validateTemplateRef(setupState)
// dynamic ref changed. unset old ref // dynamic ref changed. unset old ref
if (oldRef != null && oldRef !== ref) { if (oldRef != null && oldRef !== ref) {
if (isString(oldRef)) { if (isString(oldRef)) {
@ -87,7 +89,7 @@ export function setRef(
const doSet: SchedulerJob = () => { const doSet: SchedulerJob = () => {
if (refFor) { if (refFor) {
existing = _isString existing = _isString
? __DEV__ && hasOwn(setupState, ref) ? __DEV__ && canSetSetupRef(ref)
? setupState[ref] ? setupState[ref]
: refs[ref] : refs[ref]
: ref.value : ref.value
@ -96,7 +98,7 @@ export function setRef(
existing = [refValue] existing = [refValue]
if (_isString) { if (_isString) {
refs[ref] = existing refs[ref] = existing
if (__DEV__ && hasOwn(setupState, ref)) { if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = refs[ref] setupState[ref] = refs[ref]
// if setupState[ref] is a reactivity ref, // if setupState[ref] is a reactivity ref,
// the existing will also become reactivity too // the existing will also become reactivity too
@ -111,7 +113,7 @@ export function setRef(
} }
} else if (_isString) { } else if (_isString) {
refs[ref] = refValue refs[ref] = refValue
if (__DEV__ && hasOwn(setupState, ref)) { if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = refValue setupState[ref] = refValue
} }
} else if (_isRef) { } else if (_isRef) {
@ -129,7 +131,7 @@ export function setRef(
remove(existing, refValue) remove(existing, refValue)
} else if (_isString) { } else if (_isString) {
refs[ref] = null refs[ref] = null
if (__DEV__ && hasOwn(setupState, ref)) { if (__DEV__ && canSetSetupRef(ref)) {
setupState[ref] = null setupState[ref] = null
} }
} else if (_isRef) { } else if (_isRef) {