wip: ref v-for compat
This commit is contained in:
parent
3e815be24e
commit
86703c23a6
@ -20,6 +20,7 @@ export const enum CompilerDeprecationTypes {
|
|||||||
COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
|
COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
|
||||||
COMPILER_V_ON_NATIVE = 'COMPILER_V_ON_NATIVE',
|
COMPILER_V_ON_NATIVE = 'COMPILER_V_ON_NATIVE',
|
||||||
COMPILER_V_IF_V_FOR_PRECEDENCE = 'COMPILER_V_IF_V_FOR_PRECEDENCE',
|
COMPILER_V_IF_V_FOR_PRECEDENCE = 'COMPILER_V_IF_V_FOR_PRECEDENCE',
|
||||||
|
COMPILER_V_FOR_REF = 'COMPILER_V_FOR_REF',
|
||||||
COMPILER_NATIVE_TEMPLATE = 'COMPILER_NATIVE_TEMPLATE',
|
COMPILER_NATIVE_TEMPLATE = 'COMPILER_NATIVE_TEMPLATE',
|
||||||
COMPILER_INLINE_TEMPLATE = 'COMPILER_INLINE_TEMPLATE',
|
COMPILER_INLINE_TEMPLATE = 'COMPILER_INLINE_TEMPLATE',
|
||||||
COMPILER_FILTERS = 'COMPILER_FILTER'
|
COMPILER_FILTERS = 'COMPILER_FILTER'
|
||||||
@ -78,6 +79,13 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
|
|||||||
link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
|
link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[CompilerDeprecationTypes.COMPILER_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`
|
||||||
|
},
|
||||||
|
|
||||||
[CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE]: {
|
[CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE]: {
|
||||||
message:
|
message:
|
||||||
`<template> with no special directives will render as a native template ` +
|
`<template> with no special directives will render as a native template ` +
|
||||||
|
@ -590,6 +590,25 @@ export function buildProps(
|
|||||||
runtimeDirectives.push(prop)
|
runtimeDirectives.push(prop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
__COMPAT__ &&
|
||||||
|
prop.type === NodeTypes.ATTRIBUTE &&
|
||||||
|
prop.name === 'ref' &&
|
||||||
|
context.scopes.vFor > 0 &&
|
||||||
|
checkCompatEnabled(
|
||||||
|
CompilerDeprecationTypes.COMPILER_V_FOR_REF,
|
||||||
|
context,
|
||||||
|
prop.loc
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
properties.push(
|
||||||
|
createObjectProperty(
|
||||||
|
createSimpleExpression('refInFor', true),
|
||||||
|
createSimpleExpression('true', false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let propsExpression: PropsExpression | undefined = undefined
|
let propsExpression: PropsExpression | undefined = undefined
|
||||||
|
@ -44,6 +44,7 @@ export const enum DeprecationTypes {
|
|||||||
WATCH_ARRAY = 'WATCH_ARRAY',
|
WATCH_ARRAY = 'WATCH_ARRAY',
|
||||||
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
|
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
|
||||||
|
|
||||||
|
V_FOR_REF = 'V_FOR_REF',
|
||||||
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
|
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
|
||||||
CUSTOM_DIR = 'CUSTOM_DIR',
|
CUSTOM_DIR = 'CUSTOM_DIR',
|
||||||
|
|
||||||
@ -287,6 +288,13 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
|||||||
link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
|
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]: {
|
[DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
|
||||||
message:
|
message:
|
||||||
`Using keyCode as v-on modifier is no longer supported. ` +
|
`Using keyCode as v-on modifier is no longer supported. ` +
|
||||||
@ -447,16 +455,18 @@ export function warnDeprecation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dupKey = key + args.join('')
|
const dupKey = key + args.join('')
|
||||||
const compName = instance && formatComponentName(instance, instance.type)
|
let compId: string | number | null =
|
||||||
|
instance && formatComponentName(instance, instance.type)
|
||||||
|
if (compId === 'Anonymous' && instance) {
|
||||||
|
compId = instance.uid
|
||||||
|
}
|
||||||
|
|
||||||
// skip if the same warning is emitted for the same component type
|
// skip if the same warning is emitted for the same component type
|
||||||
if (compName !== `Anonymous`) {
|
const componentDupKey = dupKey + compId
|
||||||
const componentDupKey = dupKey + compName
|
if (componentDupKey in instanceWarned) {
|
||||||
if (componentDupKey in instanceWarned) {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
instanceWarned[componentDupKey] = true
|
|
||||||
}
|
}
|
||||||
|
instanceWarned[componentDupKey] = true
|
||||||
|
|
||||||
// same warning, but different component. skip the long message and just
|
// same warning, but different component. skip the long message and just
|
||||||
// log the key and count.
|
// log the key and count.
|
||||||
|
45
packages/runtime-core/src/compat/ref.ts
Normal file
45
packages/runtime-core/src/compat/ref.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -171,7 +171,7 @@ export function compatH(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const skipLegacyRootLevelProps = /*#__PURE__*/ makeMap(
|
const skipLegacyRootLevelProps = /*#__PURE__*/ makeMap(
|
||||||
'refInFor,staticStyle,staticClass,directives,model'
|
'staticStyle,staticClass,directives,model,hook'
|
||||||
)
|
)
|
||||||
|
|
||||||
function convertLegacyProps(
|
function convertLegacyProps(
|
||||||
@ -206,8 +206,6 @@ function convertLegacyProps(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (key === 'hook') {
|
|
||||||
// TODO
|
|
||||||
} else if (!skipLegacyRootLevelProps(key)) {
|
} else if (!skipLegacyRootLevelProps(key)) {
|
||||||
converted[key] = legacyProps[key as keyof LegacyVNodeProps]
|
converted[key] = legacyProps[key as keyof LegacyVNodeProps]
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,6 @@ import {
|
|||||||
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
import { createHydrationFunctions, RootHydrateFunction } from './hydration'
|
||||||
import { invokeDirectiveHook } from './directives'
|
import { invokeDirectiveHook } from './directives'
|
||||||
import { startMeasure, endMeasure } from './profiling'
|
import { startMeasure, endMeasure } from './profiling'
|
||||||
import { ComponentPublicInstance } from './componentPublicInstance'
|
|
||||||
import {
|
import {
|
||||||
devtoolsComponentAdded,
|
devtoolsComponentAdded,
|
||||||
devtoolsComponentRemoved,
|
devtoolsComponentRemoved,
|
||||||
@ -87,6 +86,7 @@ import { initFeatureFlags } from './featureFlags'
|
|||||||
import { isAsyncWrapper } from './apiAsyncComponent'
|
import { isAsyncWrapper } from './apiAsyncComponent'
|
||||||
import { isCompatEnabled } from './compat/compatConfig'
|
import { isCompatEnabled } from './compat/compatConfig'
|
||||||
import { DeprecationTypes } from './compat/compatConfig'
|
import { DeprecationTypes } from './compat/compatConfig'
|
||||||
|
import { registerLegacyRef } from './compat/ref'
|
||||||
|
|
||||||
export interface Renderer<HostElement = RendererElement> {
|
export interface Renderer<HostElement = RendererElement> {
|
||||||
render: RootRenderFunction<HostElement>
|
render: RootRenderFunction<HostElement>
|
||||||
@ -309,7 +309,8 @@ export const setRef = (
|
|||||||
rawRef: VNodeNormalizedRef,
|
rawRef: VNodeNormalizedRef,
|
||||||
oldRawRef: VNodeNormalizedRef | null,
|
oldRawRef: VNodeNormalizedRef | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
vnode: VNode | null
|
vnode: VNode,
|
||||||
|
isUnmount = false
|
||||||
) => {
|
) => {
|
||||||
if (isArray(rawRef)) {
|
if (isArray(rawRef)) {
|
||||||
rawRef.forEach((r, i) =>
|
rawRef.forEach((r, i) =>
|
||||||
@ -317,26 +318,25 @@ export const setRef = (
|
|||||||
r,
|
r,
|
||||||
oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
vnode
|
vnode,
|
||||||
|
isUnmount
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let value: ComponentPublicInstance | RendererNode | Record<string, any> | null
|
if (isAsyncWrapper(vnode) && !isUnmount) {
|
||||||
if (!vnode) {
|
|
||||||
// means unmount
|
|
||||||
value = null
|
|
||||||
} else if (isAsyncWrapper(vnode)) {
|
|
||||||
// when mounting async components, nothing needs to be done,
|
// when mounting async components, nothing needs to be done,
|
||||||
// because the template ref is forwarded to inner component
|
// because the template ref is forwarded to inner component
|
||||||
return
|
return
|
||||||
} else if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
|
||||||
value = vnode.component!.exposed || vnode.component!.proxy
|
|
||||||
} else {
|
|
||||||
value = vnode.el
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refValue =
|
||||||
|
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
||||||
|
? vnode.component!.exposed || vnode.component!.proxy
|
||||||
|
: vnode.el
|
||||||
|
const value = isUnmount ? null : refValue
|
||||||
|
|
||||||
const { i: owner, r: ref } = rawRef
|
const { i: owner, r: ref } = rawRef
|
||||||
if (__DEV__ && !owner) {
|
if (__DEV__ && !owner) {
|
||||||
warn(
|
warn(
|
||||||
@ -349,7 +349,7 @@ export const setRef = (
|
|||||||
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
|
||||||
|
|
||||||
// unset old ref
|
// dynamic ref changed. unset old ref
|
||||||
if (oldRef != null && oldRef !== ref) {
|
if (oldRef != null && oldRef !== ref) {
|
||||||
if (isString(oldRef)) {
|
if (isString(oldRef)) {
|
||||||
refs[oldRef] = null
|
refs[oldRef] = null
|
||||||
@ -363,7 +363,11 @@ export const setRef = (
|
|||||||
|
|
||||||
if (isString(ref)) {
|
if (isString(ref)) {
|
||||||
const doSet = () => {
|
const doSet = () => {
|
||||||
refs[ref] = value
|
if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
|
||||||
|
registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
|
||||||
|
} else {
|
||||||
|
refs[ref] = value
|
||||||
|
}
|
||||||
if (hasOwn(setupState, ref)) {
|
if (hasOwn(setupState, ref)) {
|
||||||
setupState[ref] = value
|
setupState[ref] = value
|
||||||
}
|
}
|
||||||
@ -584,7 +588,7 @@ function baseCreateRenderer(
|
|||||||
|
|
||||||
// set ref
|
// set ref
|
||||||
if (ref != null && parentComponent) {
|
if (ref != null && parentComponent) {
|
||||||
setRef(ref, n1 && n1.ref, parentSuspense, n2)
|
setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2113,7 +2117,7 @@ function baseCreateRenderer(
|
|||||||
} = vnode
|
} = vnode
|
||||||
// unset ref
|
// unset ref
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
setRef(ref, null, parentSuspense, null)
|
setRef(ref, null, parentSuspense, vnode, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
|
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
|
||||||
|
@ -44,6 +44,7 @@ import { setCompiledSlotRendering } from './helpers/renderSlot'
|
|||||||
import { convertLegacyComponent } from './compat/component'
|
import { convertLegacyComponent } from './compat/component'
|
||||||
import { convertLegacyVModelProps } from './compat/vModel'
|
import { convertLegacyVModelProps } from './compat/vModel'
|
||||||
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
import { defineLegacyVNodeProperties } from './compat/renderFn'
|
||||||
|
import { convertLegacyRefInFor } from './compat/ref'
|
||||||
|
|
||||||
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
|
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
|
||||||
__isFragment: true
|
__isFragment: true
|
||||||
@ -74,6 +75,7 @@ export type VNodeRef =
|
|||||||
export type VNodeNormalizedRefAtom = {
|
export type VNodeNormalizedRefAtom = {
|
||||||
i: ComponentInternalInstance
|
i: ComponentInternalInstance
|
||||||
r: VNodeRef
|
r: VNodeRef
|
||||||
|
f?: boolean // v2 compat only, refInFor marker
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VNodeNormalizedRef =
|
export type VNodeNormalizedRef =
|
||||||
@ -130,10 +132,12 @@ export interface VNode<
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
__v_isVNode: true
|
__v_isVNode: true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
[ReactiveFlags.SKIP]: true
|
[ReactiveFlags.SKIP]: true
|
||||||
|
|
||||||
type: VNodeTypes
|
type: VNodeTypes
|
||||||
props: (VNodeProps & ExtraProps) | null
|
props: (VNodeProps & ExtraProps) | null
|
||||||
key: string | number | null
|
key: string | number | null
|
||||||
@ -413,7 +417,7 @@ function _createVNode(
|
|||||||
|
|
||||||
const vnode: VNode = {
|
const vnode: VNode = {
|
||||||
__v_isVNode: true,
|
__v_isVNode: true,
|
||||||
[ReactiveFlags.SKIP]: true,
|
__v_skip: true,
|
||||||
type,
|
type,
|
||||||
props,
|
props,
|
||||||
key: props && normalizeKey(props),
|
key: props && normalizeKey(props),
|
||||||
@ -473,6 +477,7 @@ function _createVNode(
|
|||||||
|
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
convertLegacyVModelProps(vnode)
|
convertLegacyVModelProps(vnode)
|
||||||
|
convertLegacyRefInFor(vnode)
|
||||||
defineLegacyVNodeProperties(vnode)
|
defineLegacyVNodeProperties(vnode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +495,7 @@ export function cloneVNode<T, U>(
|
|||||||
const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
|
const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
|
||||||
const cloned: VNode = {
|
const cloned: VNode = {
|
||||||
__v_isVNode: true,
|
__v_isVNode: true,
|
||||||
[ReactiveFlags.SKIP]: true,
|
__v_skip: true,
|
||||||
type: vnode.type,
|
type: vnode.type,
|
||||||
props: mergedProps,
|
props: mergedProps,
|
||||||
key: mergedProps && normalizeKey(mergedProps),
|
key: mergedProps && normalizeKey(mergedProps),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user