feat: support ref in v-for, remove compat deprecation warnings
This commit is contained in:
@@ -936,7 +936,7 @@ describe('compiler: element transform', () => {
|
||||
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH))
|
||||
})
|
||||
|
||||
test('the binding exists (inline ref input)', () => {
|
||||
test('script setup inline mode template ref (binding exists)', () => {
|
||||
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
|
||||
inline: true,
|
||||
bindingMetadata: {
|
||||
@@ -949,31 +949,30 @@ describe('compiler: element transform', () => {
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'ref_key',
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
content: 'input',
|
||||
isStatic: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
content: 'ref',
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||
params: ['_value', '_refs'],
|
||||
body: {
|
||||
type: NodeTypes.JS_BLOCK_STATEMENT,
|
||||
body: [
|
||||
{
|
||||
content: `_refs['input'] = _value`
|
||||
},
|
||||
{
|
||||
content: 'input.value = _value'
|
||||
}
|
||||
]
|
||||
}
|
||||
content: 'input',
|
||||
isStatic: false
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
test('the binding not exists (inline ref input)', () => {
|
||||
test('script setup inline mode template ref (binding does not exist)', () => {
|
||||
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
|
||||
inline: true
|
||||
})
|
||||
@@ -983,96 +982,12 @@ describe('compiler: element transform', () => {
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'ref',
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||
params: ['_value', '_refs'],
|
||||
body: {
|
||||
type: NodeTypes.JS_BLOCK_STATEMENT,
|
||||
body: [
|
||||
{
|
||||
content: `_refs['input'] = _value`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
test('the binding not exists (inline maybe ref input)', () => {
|
||||
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
|
||||
inline: true,
|
||||
bindingMetadata: {
|
||||
input: BindingTypes.SETUP_MAYBE_REF
|
||||
}
|
||||
})
|
||||
expect(node.props).toMatchObject({
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: [
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'ref',
|
||||
content: 'input',
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||
params: ['_value', '_refs'],
|
||||
body: {
|
||||
type: NodeTypes.JS_BLOCK_STATEMENT,
|
||||
body: [
|
||||
{
|
||||
content: `_refs['input'] = _value`
|
||||
},
|
||||
{
|
||||
content: '_isRef(input) && (input.value = _value)'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
test('the binding not exists (inline let ref input)', () => {
|
||||
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
|
||||
inline: true,
|
||||
bindingMetadata: {
|
||||
input: BindingTypes.SETUP_LET
|
||||
}
|
||||
})
|
||||
expect(node.props).toMatchObject({
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: [
|
||||
{
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'ref',
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||
params: ['_value', '_refs'],
|
||||
body: {
|
||||
type: NodeTypes.JS_BLOCK_STATEMENT,
|
||||
body: [
|
||||
{
|
||||
content: `_refs['input'] = _value`
|
||||
},
|
||||
{
|
||||
content:
|
||||
'_isRef(input) ? input.value = _value : input = _value'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -20,7 +20,6 @@ export const enum CompilerDeprecationTypes {
|
||||
COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
|
||||
COMPILER_V_ON_NATIVE = 'COMPILER_V_ON_NATIVE',
|
||||
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_INLINE_TEMPLATE = 'COMPILER_INLINE_TEMPLATE',
|
||||
COMPILER_FILTERS = 'COMPILER_FILTER'
|
||||
@@ -79,13 +78,6 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
|
||||
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]: {
|
||||
message:
|
||||
`<template> with no special directives will render as a native template ` +
|
||||
|
||||
@@ -19,10 +19,7 @@ import {
|
||||
TemplateTextChildNode,
|
||||
DirectiveArguments,
|
||||
createVNodeCall,
|
||||
ConstantTypes,
|
||||
JSChildNode,
|
||||
createFunctionExpression,
|
||||
createBlockStatement
|
||||
ConstantTypes
|
||||
} from '../ast'
|
||||
import {
|
||||
PatchFlags,
|
||||
@@ -48,8 +45,7 @@ import {
|
||||
KEEP_ALIVE,
|
||||
SUSPENSE,
|
||||
UNREF,
|
||||
GUARD_REACTIVE_PROPS,
|
||||
IS_REF
|
||||
GUARD_REACTIVE_PROPS
|
||||
} from '../runtimeHelpers'
|
||||
import {
|
||||
getInnerRange,
|
||||
@@ -467,20 +463,32 @@ export function buildProps(
|
||||
const prop = props[i]
|
||||
if (prop.type === NodeTypes.ATTRIBUTE) {
|
||||
const { loc, name, value } = prop
|
||||
let valueNode = createSimpleExpression(
|
||||
value ? value.content : '',
|
||||
true,
|
||||
value ? value.loc : loc
|
||||
) as JSChildNode
|
||||
let isStatic = true
|
||||
if (name === 'ref') {
|
||||
hasRef = true
|
||||
if (context.scopes.vFor > 0) {
|
||||
properties.push(
|
||||
createObjectProperty(
|
||||
createSimpleExpression('ref_for', true),
|
||||
createSimpleExpression('true')
|
||||
)
|
||||
)
|
||||
}
|
||||
// in inline mode there is no setupState object, so we can't use string
|
||||
// keys to set the ref. Instead, we need to transform it to pass the
|
||||
// actual ref instead.
|
||||
if (!__BROWSER__ && context.inline && value?.content) {
|
||||
valueNode = createFunctionExpression(['_value', '_refs'])
|
||||
valueNode.body = createBlockStatement(
|
||||
processInlineRef(context, value.content)
|
||||
if (
|
||||
!__BROWSER__ &&
|
||||
value &&
|
||||
context.inline &&
|
||||
context.bindingMetadata[value.content]
|
||||
) {
|
||||
isStatic = false
|
||||
properties.push(
|
||||
createObjectProperty(
|
||||
createSimpleExpression('ref_key', true),
|
||||
createSimpleExpression(value.content, true, value.loc)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -504,7 +512,11 @@ export function buildProps(
|
||||
true,
|
||||
getInnerRange(loc, 0, name.length)
|
||||
),
|
||||
valueNode
|
||||
createSimpleExpression(
|
||||
value ? value.content : '',
|
||||
isStatic,
|
||||
value ? value.loc : loc
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@@ -555,6 +567,15 @@ export function buildProps(
|
||||
shouldUseBlock = true
|
||||
}
|
||||
|
||||
if (isVBind && isStaticArgOf(arg, 'ref') && context.scopes.vFor > 0) {
|
||||
properties.push(
|
||||
createObjectProperty(
|
||||
createSimpleExpression('ref_for', true),
|
||||
createSimpleExpression('true')
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// special case for v-bind and v-on with no argument
|
||||
if (!arg && (isVBind || isVOn)) {
|
||||
hasDynamicKeys = true
|
||||
@@ -654,25 +675,6 @@ export function buildProps(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -914,30 +916,3 @@ function stringifyDynamicPropNames(props: string[]): string {
|
||||
function isComponentTag(tag: string) {
|
||||
return tag === 'component' || tag === 'Component'
|
||||
}
|
||||
|
||||
function processInlineRef(
|
||||
context: TransformContext,
|
||||
raw: string
|
||||
): JSChildNode[] {
|
||||
const body = [createSimpleExpression(`_refs['${raw}'] = _value`)]
|
||||
const { bindingMetadata, helperString } = context
|
||||
const type = bindingMetadata[raw]
|
||||
if (type === BindingTypes.SETUP_REF) {
|
||||
body.push(createSimpleExpression(`${raw}.value = _value`))
|
||||
} else if (type === BindingTypes.SETUP_MAYBE_REF) {
|
||||
body.push(
|
||||
createSimpleExpression(
|
||||
`${helperString(IS_REF)}(${raw}) && (${raw}.value = _value)`
|
||||
)
|
||||
)
|
||||
} else if (type === BindingTypes.SETUP_LET) {
|
||||
body.push(
|
||||
createSimpleExpression(
|
||||
`${helperString(
|
||||
IS_REF
|
||||
)}(${raw}) ? ${raw}.value = _value : ${raw} = _value`
|
||||
)
|
||||
)
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user