Просмотр исходного кода

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

close #13665

align to bc63df0
edison 8 месяцев назад
Родитель
Сommit
e56997f0d5

+ 4 - 0
packages/runtime-core/src/index.ts

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

+ 29 - 20
packages/runtime-core/src/rendererTemplateRef.ts

@@ -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)
+      }
+}

+ 43 - 2
packages/runtime-vapor/__tests__/dom/templateRef.spec.ts

@@ -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 () => {

+ 6 - 4
packages/runtime-vapor/src/apiTemplateRef.ts

@@ -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) {