]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): fix readonly warning when useTemplateRef has same variable name...
authoredison <daiwei521@126.com>
Wed, 20 Aug 2025 09:50:04 +0000 (17:50 +0800)
committerGitHub <noreply@github.com>
Wed, 20 Aug 2025 09:50:04 +0000 (17:50 +0800)
close #13665

align to bc63df0

packages/runtime-core/src/index.ts
packages/runtime-core/src/rendererTemplateRef.ts
packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
packages/runtime-vapor/src/apiTemplateRef.ts

index 243bde548c5cff327235a164abcb6aa4cd8e5a79..9d97bb18593ca93a388011aedabb0de5c5b4ac72 100644 (file)
@@ -562,3 +562,7 @@ export { initFeatureFlags } from './featureFlags'
  * @internal
  */
 export { createInternalObject } from './internalObject'
+/**
+ * @internal
+ */
+export { createCanSetSetupRefChecker } from './rendererTemplateRef'
index 31fcf8c2d5b2c227cfd39f636efddf1220008841..84f0bda1b95bead1f62e7647d7d0e80bd344bb8e 100644 (file)
@@ -14,7 +14,11 @@ import { warn } from './warning'
 import { isRef, toRaw } from '@vue/reactivity'
 import { ErrorCodes, callWithErrorHandling } from './errorHandling'
 import { queuePostRenderEffect } from './renderer'
-import { type ComponentOptions, getComponentPublicInstance } from './component'
+import {
+  type ComponentOptions,
+  type Data,
+  getComponentPublicInstance,
+} from './component'
 import { knownTemplateRefs } from './helpers/useTemplateRef'
 
 /**
@@ -73,25 +77,7 @@ export function setRef(
   const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
   const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
   const setupState = owner.setupState
-  const rawSetupState = toRaw(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)
-        }
+  const canSetSetupRef = createCanSetSetupRefChecker(setupState)
 
   // dynamic ref changed. unset old ref
   if (oldRef != null && oldRef !== ref) {
@@ -161,3 +147,26 @@ export function setRef(
     }
   }
 }
+
+export function createCanSetSetupRefChecker(
+  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)
+      }
+}
index f1ce23ac156b5ceddd4238063c14245c8057892e..4e8a1a9253c2a41d07d673c55eb25b5d5d2d93bc 100644 (file)
@@ -19,6 +19,7 @@ import {
   nextTick,
   reactive,
   ref,
+  shallowRef,
   useTemplateRef,
   watchEffect,
 } from '@vue/runtime-dom'
@@ -208,8 +209,8 @@ describe('api: template ref', () => {
     const { render } = define({
       setup() {
         return {
-          foo: fooEl,
-          bar: barEl,
+          foo: shallowRef(fooEl),
+          bar: shallowRef(barEl),
         }
       },
       render() {
@@ -251,6 +252,7 @@ describe('api: template ref', () => {
     })
     const { host } = render()
     expect(state.refKey).toBe(host.children[0])
+    expect('Template ref "refKey" used on a non-ref value').toHaveBeenWarned()
   })
 
   test('multiple root refs', () => {
@@ -713,6 +715,45 @@ describe('api: template ref', () => {
     expect(html()).toBe('<div>changed</div><!--dynamic-component-->')
   })
 
+  test('should not attempt to set when variable name is same as key', () => {
+    let tRef: ShallowRef
+    const key = 'refKey'
+    define({
+      setup() {
+        tRef = useTemplateRef('_')
+        return {
+          [key]: tRef,
+        }
+      },
+      render() {
+        const n0 = template('<div></div>')() as Element
+        createTemplateRefSetter()(n0, key)
+        return n0
+      },
+    }).render()
+    expect('target is readonly').not.toHaveBeenWarned()
+    expect(tRef!.value).toBe(null)
+  })
+
+  test('should work when used as direct ref value (compiled in prod mode)', () => {
+    __DEV__ = false
+    try {
+      let foo: ShallowRef
+      const { host } = define({
+        setup() {
+          foo = useTemplateRef('foo')
+          const n0 = template('<div></div>')() as Element
+          createTemplateRefSetter()(n0, foo)
+          return n0
+        },
+      }).render()
+      expect('target is readonly').not.toHaveBeenWarned()
+      expect(foo!.value).toBe(host.children[0])
+    } finally {
+      __DEV__ = true
+    }
+  })
+
   // TODO: can not reproduce in Vapor
   // // #2078
   // test('handling multiple merged refs', async () => {
index 7a30d219811b8d559561164d404911ea85544ca4..a14f373e7def428d3dc890c5ff61ddcfeb930077 100644 (file)
@@ -9,6 +9,7 @@ import {
   ErrorCodes,
   type SchedulerJob,
   callWithErrorHandling,
+  createCanSetSetupRefChecker,
   queuePostFlushCb,
   warn,
 } from '@vue/runtime-dom'
@@ -55,6 +56,7 @@ export function setRef(
   const refs =
     instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs
 
+  const canSetSetupRef = createCanSetSetupRefChecker(setupState)
   // dynamic ref changed. unset old ref
   if (oldRef != null && oldRef !== ref) {
     if (isString(oldRef)) {
@@ -87,7 +89,7 @@ export function setRef(
       const doSet: SchedulerJob = () => {
         if (refFor) {
           existing = _isString
-            ? __DEV__ && hasOwn(setupState, ref)
+            ? __DEV__ && canSetSetupRef(ref)
               ? setupState[ref]
               : refs[ref]
             : ref.value
@@ -96,7 +98,7 @@ export function setRef(
             existing = [refValue]
             if (_isString) {
               refs[ref] = existing
-              if (__DEV__ && hasOwn(setupState, ref)) {
+              if (__DEV__ && canSetSetupRef(ref)) {
                 setupState[ref] = refs[ref]
                 // if setupState[ref] is a reactivity ref,
                 // the existing will also become reactivity too
@@ -111,7 +113,7 @@ export function setRef(
           }
         } else if (_isString) {
           refs[ref] = refValue
-          if (__DEV__ && hasOwn(setupState, ref)) {
+          if (__DEV__ && canSetSetupRef(ref)) {
             setupState[ref] = refValue
           }
         } else if (_isRef) {
@@ -129,7 +131,7 @@ export function setRef(
             remove(existing, refValue)
           } else if (_isString) {
             refs[ref] = null
-            if (__DEV__ && hasOwn(setupState, ref)) {
+            if (__DEV__ && canSetSetupRef(ref)) {
               setupState[ref] = null
             }
           } else if (_isRef) {