Skip to content
Permalink
Browse files
feat: support ref in v-for, remove compat deprecation warnings
  • Loading branch information
yyx990803 committed Dec 10, 2021
1 parent a1167c5 commit 41c18effea9dd32ab899b5de3bb0513abdb52ee4
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 326 deletions.
@@ -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,130 +949,45 @@ describe('compiler: element transform', () => {
{
type: NodeTypes.JS_PROPERTY,
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'ref',
content: 'ref_key',
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'
}
]
}
}
}
]
})
})

test('the binding not exists (inline ref input)', () => {
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
inline: true
})
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`
}
]
}
}
}
]
})
})

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',
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)'
}
]
}
content: 'input',
isStatic: false
}
}
]
})
})

test('the binding not exists (inline let ref input)', () => {
test('script setup inline mode template ref (binding does not exist)', () => {
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
inline: true,
bindingMetadata: {
input: BindingTypes.SETUP_LET
}
inline: true
})
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'
}
]
}
content: 'input',
isStatic: true
}
}
]
@@ -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
}

0 comments on commit 41c18ef

Please sign in to comment.