mirror of https://github.com/vuejs/core.git
fix(useTemplateRef): properly fix readonly warning in dev and ensure prod behavior consistency
close #11808 close #11816 close #11810
This commit is contained in:
parent
46c3ab1d71
commit
9b7797d0d1
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
type ShallowRef,
|
||||||
h,
|
h,
|
||||||
nextTick,
|
nextTick,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
|
@ -84,12 +85,12 @@ describe('useTemplateRef', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// #11795
|
// #11795
|
||||||
test('should work when variable name is same as key', () => {
|
test('should not attempt to set when variable name is same as key', () => {
|
||||||
let tRef
|
let tRef: ShallowRef
|
||||||
const key = 'refKey'
|
const key = 'refKey'
|
||||||
const Comp = {
|
const Comp = {
|
||||||
setup() {
|
setup() {
|
||||||
tRef = useTemplateRef(key)
|
tRef = useTemplateRef('_')
|
||||||
return {
|
return {
|
||||||
[key]: tRef,
|
[key]: tRef,
|
||||||
}
|
}
|
||||||
|
@ -102,5 +103,26 @@ describe('useTemplateRef', () => {
|
||||||
render(h(Comp), root)
|
render(h(Comp), root)
|
||||||
|
|
||||||
expect('target is readonly').not.toHaveBeenWarned()
|
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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { getCurrentInstance } from '../component'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { EMPTY_OBJ } from '@vue/shared'
|
import { EMPTY_OBJ } from '@vue/shared'
|
||||||
|
|
||||||
|
export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()
|
||||||
|
|
||||||
export function useTemplateRef<T = unknown, Keys extends string = string>(
|
export function useTemplateRef<T = unknown, Keys extends string = string>(
|
||||||
key: Keys,
|
key: Keys,
|
||||||
): Readonly<ShallowRef<T | null>> {
|
): Readonly<ShallowRef<T | null>> {
|
||||||
|
@ -10,7 +12,6 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
|
||||||
const r = shallowRef(null)
|
const r = shallowRef(null)
|
||||||
if (i) {
|
if (i) {
|
||||||
const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
|
const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
|
||||||
|
|
||||||
let desc: PropertyDescriptor | undefined
|
let desc: PropertyDescriptor | undefined
|
||||||
if (
|
if (
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
|
@ -31,5 +32,9 @@ export function useTemplateRef<T = unknown, Keys extends string = string>(
|
||||||
`instance to be associated with.`,
|
`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
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ import {
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { isAsyncWrapper } from './apiAsyncComponent'
|
import { isAsyncWrapper } from './apiAsyncComponent'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { isRef } from '@vue/reactivity'
|
import { isRef, toRaw } from '@vue/reactivity'
|
||||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||||
import type { SchedulerJob } from './scheduler'
|
import type { SchedulerJob } from './scheduler'
|
||||||
import { queuePostRenderEffect } from './renderer'
|
import { queuePostRenderEffect } from './renderer'
|
||||||
import { getComponentPublicInstance } from './component'
|
import { getComponentPublicInstance } from './component'
|
||||||
|
import { knownTemplateRefs } from './helpers/useTemplateRef'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function for handling a template ref
|
* Function for handling a template ref
|
||||||
|
@ -63,12 +64,16 @@ 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 =
|
const canSetSetupRef =
|
||||||
setupState === EMPTY_OBJ
|
setupState === EMPTY_OBJ
|
||||||
? () => false
|
? () => false
|
||||||
: (key: string) =>
|
: (key: string) => {
|
||||||
hasOwn(setupState, key) &&
|
if (__DEV__ && knownTemplateRefs.has(rawSetupState[key] as any)) {
|
||||||
!(Object.getOwnPropertyDescriptor(refs, key) || EMPTY_OBJ).get
|
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) {
|
||||||
|
|
Loading…
Reference in New Issue