From 227bd9bdfef2f6ed1b56369a6a31e46f4781a612 Mon Sep 17 00:00:00 2001 From: tycho Date: Wed, 4 Sep 2024 15:35:08 +0800 Subject: [PATCH] fix(runtime-core): prevent readonly warning when using `useTemplateRef` with same variable name as template ref --- .../__tests__/helpers/useTemplateRef.spec.ts | 8 ++++++-- packages/runtime-core/src/helpers/useTemplateRef.ts | 11 +++++++++++ packages/runtime-core/src/rendererTemplateRef.ts | 13 ++++++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts b/packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts index 9e4137da80f..cbc74ca8a91 100644 --- a/packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts +++ b/packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts @@ -27,12 +27,16 @@ describe('useTemplateRef', () => { test('should be readonly', () => { let tRef const key = 'refKey' + const key2 = 'foo' const Comp = { setup() { tRef = useTemplateRef(key) + // naming a variable declared with the same as a template ref + const foo = useTemplateRef(key2) + return { [key2]: foo } }, render() { - return h('div', { ref: key }) + return h('div', { ref: key }, [h('div', { ref: key2 })]) }, } const root = nodeOps.createElement('div') @@ -42,7 +46,7 @@ describe('useTemplateRef', () => { tRef.value = 123 expect(tRef!.value).toBe(root.children[0]) - expect('target is readonly').toHaveBeenWarned() + expect('target is readonly').toHaveBeenWarnedTimes(1) }) test('should be updated for ref of dynamic strings', async () => { diff --git a/packages/runtime-core/src/helpers/useTemplateRef.ts b/packages/runtime-core/src/helpers/useTemplateRef.ts index 58c109a9246..5e9a9df3c28 100644 --- a/packages/runtime-core/src/helpers/useTemplateRef.ts +++ b/packages/runtime-core/src/helpers/useTemplateRef.ts @@ -3,6 +3,8 @@ import { getCurrentInstance } from '../component' import { warn } from '../warning' import { EMPTY_OBJ } from '@vue/shared' +export const TEMPLATE_REF_KEYS = '__v_ref_keys' + export function useTemplateRef( key: Keys, ): Readonly> { @@ -19,6 +21,15 @@ export function useTemplateRef( ) { warn(`useTemplateRef('${key}') already exists.`) } else { + if (refs[TEMPLATE_REF_KEYS]) { + ;(refs[TEMPLATE_REF_KEYS] as any).add(key) + } else { + const set = new Set([key]) + Object.defineProperty(refs, TEMPLATE_REF_KEYS, { + get: () => set, + }) + } + Object.defineProperty(refs, key, { enumerable: true, get: () => r.value, diff --git a/packages/runtime-core/src/rendererTemplateRef.ts b/packages/runtime-core/src/rendererTemplateRef.ts index 647ce1fb42f..8c205cd7dff 100644 --- a/packages/runtime-core/src/rendererTemplateRef.ts +++ b/packages/runtime-core/src/rendererTemplateRef.ts @@ -16,6 +16,7 @@ import { ErrorCodes, callWithErrorHandling } from './errorHandling' import type { SchedulerJob } from './scheduler' import { queuePostRenderEffect } from './renderer' import { getComponentPublicInstance } from './component' +import { TEMPLATE_REF_KEYS } from './helpers/useTemplateRef' /** * Function for handling a template ref @@ -64,11 +65,17 @@ export function setRef( const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs const setupState = owner.setupState + const canUpdateSetupState = (ref: string) => { + if (!hasOwn(setupState, ref)) return false + const set: Set | undefined = refs[TEMPLATE_REF_KEYS] as any + return !set || !set.has(ref) + } + // dynamic ref changed. unset old ref if (oldRef != null && oldRef !== ref) { if (isString(oldRef)) { refs[oldRef] = null - if (hasOwn(setupState, oldRef)) { + if (canUpdateSetupState(oldRef)) { setupState[oldRef] = null } } else if (isRef(oldRef)) { @@ -95,7 +102,7 @@ export function setRef( if (!isArray(existing)) { if (_isString) { refs[ref] = [refValue] - if (hasOwn(setupState, ref)) { + if (canUpdateSetupState(ref)) { setupState[ref] = refs[ref] } } else { @@ -108,7 +115,7 @@ export function setRef( } } else if (_isString) { refs[ref] = value - if (hasOwn(setupState, ref)) { + if (canUpdateSetupState(ref)) { setupState[ref] = value } } else if (_isRef) {