]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(useTemplateRef): properly fix readonly warning in dev and ensure prod behavior...
authorEvan You <evan@vuejs.org>
Thu, 5 Sep 2024 09:46:42 +0000 (17:46 +0800)
committerEvan You <evan@vuejs.org>
Thu, 5 Sep 2024 09:47:03 +0000 (17:47 +0800)
close #11808
close #11816
close #11810

packages/runtime-core/__tests__/helpers/useTemplateRef.spec.ts
packages/runtime-core/src/helpers/useTemplateRef.ts
packages/runtime-core/src/rendererTemplateRef.ts

index 10de6f483532865da9aa75ac68409b818a51305e..adc8ed66c7789c76a14462ea8ddef2f766fa660f 100644 (file)
@@ -1,4 +1,5 @@
 import {
+  type ShallowRef,
   h,
   nextTick,
   nodeOps,
@@ -84,12 +85,12 @@ describe('useTemplateRef', () => {
   })
 
   // #11795
-  test('should work when variable name is same as key', () => {
-    let tRef
+  test('should not attempt to set when variable name is same as key', () => {
+    let tRef: ShallowRef
     const key = 'refKey'
     const Comp = {
       setup() {
-        tRef = useTemplateRef(key)
+        tRef = useTemplateRef('_')
         return {
           [key]: tRef,
         }
@@ -102,5 +103,26 @@ describe('useTemplateRef', () => {
     render(h(Comp), root)
 
     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 Comp = {
+        setup() {
+          foo = useTemplateRef('foo')
+          return () => h('div', { ref: foo })
+        },
+      }
+      const root = nodeOps.createElement('div')
+      render(h(Comp), root)
+
+      expect('target is readonly').not.toHaveBeenWarned()
+      expect(foo!.value).toBe(root.children[0])
+    } finally {
+      __DEV__ = true
+    }
   })
 })
index 58c109a9246962c3baaf057a2f6268bdb9da4434..4cb10ea8139e462c9d9582c62f00531f45563240 100644 (file)
@@ -3,6 +3,8 @@ import { getCurrentInstance } from '../component'
 import { warn } from '../warning'
 import { EMPTY_OBJ } from '@vue/shared'
 
+export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()
+
 export function useTemplateRef<T = unknown, Keys extends string = string>(
   key: Keys,
 ): Readonly<ShallowRef<T | null>> {
@@ -10,7 +12,6 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
   const r = shallowRef(null)
   if (i) {
     const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
-
     let desc: PropertyDescriptor | undefined
     if (
       __DEV__ &&
@@ -31,5 +32,9 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
         `instance to be associated with.`,
     )
   }
-  return (__DEV__ ? readonly(r) : r) as any
+  const ret = __DEV__ ? readonly(r) : r
+  if (__DEV__) {
+    knownTemplateRefs.add(ret)
+  }
+  return ret
 }
index c7b15fe40221b83f1a0f2d28f76b5ac4b7ef2779..1ffe3035794da8adbee967ad749c0f0eec0df33b 100644 (file)
@@ -11,11 +11,12 @@ import {
 } from '@vue/shared'
 import { isAsyncWrapper } from './apiAsyncComponent'
 import { warn } from './warning'
-import { isRef } from '@vue/reactivity'
+import { isRef, toRaw } from '@vue/reactivity'
 import { ErrorCodes, callWithErrorHandling } from './errorHandling'
 import type { SchedulerJob } from './scheduler'
 import { queuePostRenderEffect } from './renderer'
 import { getComponentPublicInstance } from './component'
+import { knownTemplateRefs } from './helpers/useTemplateRef'
 
 /**
  * Function for handling a template ref
@@ -63,12 +64,16 @@ 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) =>
-          hasOwn(setupState, key) &&
-          !(Object.getOwnPropertyDescriptor(refs, key) || EMPTY_OBJ).get
+      : (key: string) => {
+          if (__DEV__ && knownTemplateRefs.has(rawSetupState[key] as any)) {
+            return false
+          }
+          return hasOwn(rawSetupState, key)
+        }
 
   // dynamic ref changed. unset old ref
   if (oldRef != null && oldRef !== ref) {