feat: support ref in v-for, remove compat deprecation warnings
This commit is contained in:
@@ -46,7 +46,6 @@ export const enum DeprecationTypes {
|
||||
WATCH_ARRAY = 'WATCH_ARRAY',
|
||||
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
|
||||
|
||||
V_FOR_REF = 'V_FOR_REF',
|
||||
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
|
||||
CUSTOM_DIR = 'CUSTOM_DIR',
|
||||
|
||||
@@ -298,13 +297,6 @@ export const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
|
||||
},
|
||||
|
||||
[DeprecationTypes.V_FOR_REF]: {
|
||||
message:
|
||||
`Ref usage on v-for no longer creates array ref values in Vue 3. ` +
|
||||
`Consider using function refs or refactor to avoid ref usage altogether.`,
|
||||
link: `https://v3.vuejs.org/guide/migration/array-refs.html`
|
||||
},
|
||||
|
||||
[DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
|
||||
message:
|
||||
`Using keyCode as v-on modifier is no longer supported. ` +
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { isArray, remove } from '@vue/shared'
|
||||
import { ComponentInternalInstance, Data } from '../component'
|
||||
import { VNode } from '../vnode'
|
||||
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
||||
|
||||
export function convertLegacyRefInFor(vnode: VNode) {
|
||||
// refInFor
|
||||
if (vnode.props && vnode.props.refInFor) {
|
||||
delete vnode.props.refInFor
|
||||
if (vnode.ref) {
|
||||
if (isArray(vnode.ref)) {
|
||||
vnode.ref.forEach(r => (r.f = true))
|
||||
} else {
|
||||
vnode.ref.f = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function registerLegacyRef(
|
||||
refs: Data,
|
||||
key: string,
|
||||
value: any,
|
||||
owner: ComponentInternalInstance,
|
||||
isInFor: boolean | undefined,
|
||||
isUnmount: boolean
|
||||
) {
|
||||
const existing = refs[key]
|
||||
if (isUnmount) {
|
||||
if (isArray(existing)) {
|
||||
remove(existing, value)
|
||||
} else {
|
||||
refs[key] = null
|
||||
}
|
||||
} else if (isInFor) {
|
||||
__DEV__ && warnDeprecation(DeprecationTypes.V_FOR_REF, owner)
|
||||
if (!isArray(existing)) {
|
||||
refs[key] = [value]
|
||||
} else if (!existing.includes(value)) {
|
||||
existing.push(value)
|
||||
}
|
||||
} else {
|
||||
refs[key] = value
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,8 @@ import {
|
||||
hasOwn,
|
||||
invokeArrayFns,
|
||||
isArray,
|
||||
getGlobalThis
|
||||
getGlobalThis,
|
||||
remove
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
queueJob,
|
||||
@@ -86,7 +87,6 @@ import { initFeatureFlags } from './featureFlags'
|
||||
import { isAsyncWrapper } from './apiAsyncComponent'
|
||||
import { isCompatEnabled } from './compat/compatConfig'
|
||||
import { DeprecationTypes } from './compat/compatConfig'
|
||||
import { registerLegacyRef } from './compat/ref'
|
||||
|
||||
export interface Renderer<HostElement = RendererElement> {
|
||||
render: RootRenderFunction<HostElement>
|
||||
@@ -2407,40 +2407,53 @@ export function setRef(
|
||||
}
|
||||
}
|
||||
|
||||
if (isString(ref)) {
|
||||
const doSet = () => {
|
||||
if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
|
||||
registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
|
||||
} else {
|
||||
refs[ref] = value
|
||||
}
|
||||
if (hasOwn(setupState, ref)) {
|
||||
setupState[ref] = value
|
||||
}
|
||||
}
|
||||
// #1789: for non-null values, set them after render
|
||||
// null values means this is unmount and it should not overwrite another
|
||||
// ref with the same key
|
||||
if (value) {
|
||||
;(doSet as SchedulerJob).id = -1
|
||||
queuePostRenderEffect(doSet, parentSuspense)
|
||||
} else {
|
||||
doSet()
|
||||
}
|
||||
} else if (isRef(ref)) {
|
||||
const doSet = () => {
|
||||
ref.value = value
|
||||
}
|
||||
if (value) {
|
||||
;(doSet as SchedulerJob).id = -1
|
||||
queuePostRenderEffect(doSet, parentSuspense)
|
||||
} else {
|
||||
doSet()
|
||||
}
|
||||
} else if (isFunction(ref)) {
|
||||
if (isFunction(ref)) {
|
||||
callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
|
||||
} else if (__DEV__) {
|
||||
warn('Invalid template ref type:', value, `(${typeof value})`)
|
||||
} else {
|
||||
const _isString = isString(ref)
|
||||
const _isRef = isRef(ref)
|
||||
if (_isString || _isRef) {
|
||||
const doSet = () => {
|
||||
if (rawRef.f) {
|
||||
const existing = _isString ? refs[ref] : ref.value
|
||||
if (isUnmount) {
|
||||
isArray(existing) && remove(existing, refValue)
|
||||
} else {
|
||||
if (!isArray(existing)) {
|
||||
if (_isString) {
|
||||
refs[ref] = [refValue]
|
||||
} else {
|
||||
ref.value = [refValue]
|
||||
if (rawRef.k) refs[rawRef.k] = ref.value
|
||||
}
|
||||
} else if (!existing.includes(refValue)) {
|
||||
existing.push(refValue)
|
||||
}
|
||||
}
|
||||
} else if (_isString) {
|
||||
refs[ref] = value
|
||||
if (hasOwn(setupState, ref)) {
|
||||
setupState[ref] = value
|
||||
}
|
||||
} else if (isRef(ref)) {
|
||||
ref.value = value
|
||||
if (rawRef.k) refs[rawRef.k] = value
|
||||
} else if (__DEV__) {
|
||||
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
||||
}
|
||||
}
|
||||
if (value) {
|
||||
// #1789: for non-null values, set them after render
|
||||
// null values means this is unmount and it should not overwrite another
|
||||
// ref with the same key
|
||||
;(doSet as SchedulerJob).id = -1
|
||||
queuePostRenderEffect(doSet, parentSuspense)
|
||||
} else {
|
||||
doSet()
|
||||
}
|
||||
} else if (__DEV__) {
|
||||
warn('Invalid template ref type:', ref, `(${typeof ref})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ import { hmrDirtyComponents } from './hmr'
|
||||
import { convertLegacyComponent } from './compat/component'
|
||||
import { convertLegacyVModelProps } from './compat/componentVModel'
|
||||
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
||||
import { convertLegacyRefInFor } from './compat/ref'
|
||||
|
||||
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
|
||||
__isFragment: true
|
||||
@@ -73,7 +72,8 @@ export type VNodeRef =
|
||||
export type VNodeNormalizedRefAtom = {
|
||||
i: ComponentInternalInstance
|
||||
r: VNodeRef
|
||||
f?: boolean // v2 compat only, refInFor marker
|
||||
k?: string // setup ref key
|
||||
f?: boolean // refInFor marker
|
||||
}
|
||||
|
||||
export type VNodeNormalizedRef =
|
||||
@@ -92,6 +92,8 @@ export type VNodeHook =
|
||||
export type VNodeProps = {
|
||||
key?: string | number | symbol
|
||||
ref?: VNodeRef
|
||||
ref_for?: boolean
|
||||
ref_key?: string
|
||||
|
||||
// vnode hooks
|
||||
onVnodeBeforeMount?: VNodeMountHook | VNodeMountHook[]
|
||||
@@ -380,11 +382,15 @@ export const InternalObjectKey = `__vInternal`
|
||||
const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
|
||||
key != null ? key : null
|
||||
|
||||
const normalizeRef = ({ ref }: VNodeProps): VNodeNormalizedRefAtom | null => {
|
||||
const normalizeRef = ({
|
||||
ref,
|
||||
ref_key,
|
||||
ref_for
|
||||
}: VNodeProps): VNodeNormalizedRefAtom | null => {
|
||||
return (
|
||||
ref != null
|
||||
? isString(ref) || isRef(ref) || isFunction(ref)
|
||||
? { i: currentRenderingInstance, r: ref }
|
||||
? { i: currentRenderingInstance, r: ref, k: ref_key, f: !!ref_for }
|
||||
: ref
|
||||
: null
|
||||
) as any
|
||||
@@ -468,7 +474,6 @@ function createBaseVNode(
|
||||
|
||||
if (__COMPAT__) {
|
||||
convertLegacyVModelProps(vnode)
|
||||
convertLegacyRefInFor(vnode)
|
||||
defineLegacyVNodeProperties(vnode)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user