Skip to content

Commit 41c18ef

Browse files
committedDec 10, 2021
feat: support ref in v-for, remove compat deprecation warnings
1 parent a1167c5 commit 41c18ef

File tree

11 files changed

+216
-326
lines changed

11 files changed

+216
-326
lines changed
 

‎packages/compiler-core/__tests__/transforms/transformElement.spec.ts

+10-95
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ describe('compiler: element transform', () => {
936936
expect(node.patchFlag).toBe(genFlagText(PatchFlags.NEED_PATCH))
937937
})
938938

939-
test('the binding exists (inline ref input)', () => {
939+
test('script setup inline mode template ref (binding exists)', () => {
940940
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
941941
inline: true,
942942
bindingMetadata: {
@@ -949,130 +949,45 @@ describe('compiler: element transform', () => {
949949
{
950950
type: NodeTypes.JS_PROPERTY,
951951
key: {
952-
type: NodeTypes.SIMPLE_EXPRESSION,
953-
content: 'ref',
952+
content: 'ref_key',
954953
isStatic: true
955954
},
956955
value: {
957-
type: NodeTypes.JS_FUNCTION_EXPRESSION,
958-
params: ['_value', '_refs'],
959-
body: {
960-
type: NodeTypes.JS_BLOCK_STATEMENT,
961-
body: [
962-
{
963-
content: `_refs['input'] = _value`
964-
},
965-
{
966-
content: 'input.value = _value'
967-
}
968-
]
969-
}
970-
}
971-
}
972-
]
973-
})
974-
})
975-
976-
test('the binding not exists (inline ref input)', () => {
977-
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
978-
inline: true
979-
})
980-
expect(node.props).toMatchObject({
981-
type: NodeTypes.JS_OBJECT_EXPRESSION,
982-
properties: [
983-
{
984-
type: NodeTypes.JS_PROPERTY,
985-
key: {
986-
type: NodeTypes.SIMPLE_EXPRESSION,
987-
content: 'ref',
956+
content: 'input',
988957
isStatic: true
989-
},
990-
value: {
991-
type: NodeTypes.JS_FUNCTION_EXPRESSION,
992-
params: ['_value', '_refs'],
993-
body: {
994-
type: NodeTypes.JS_BLOCK_STATEMENT,
995-
body: [
996-
{
997-
content: `_refs['input'] = _value`
998-
}
999-
]
1000-
}
1001958
}
1002-
}
1003-
]
1004-
})
1005-
})
1006-
1007-
test('the binding not exists (inline maybe ref input)', () => {
1008-
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
1009-
inline: true,
1010-
bindingMetadata: {
1011-
input: BindingTypes.SETUP_MAYBE_REF
1012-
}
1013-
})
1014-
expect(node.props).toMatchObject({
1015-
type: NodeTypes.JS_OBJECT_EXPRESSION,
1016-
properties: [
959+
},
1017960
{
1018961
type: NodeTypes.JS_PROPERTY,
1019962
key: {
1020-
type: NodeTypes.SIMPLE_EXPRESSION,
1021963
content: 'ref',
1022964
isStatic: true
1023965
},
1024966
value: {
1025-
type: NodeTypes.JS_FUNCTION_EXPRESSION,
1026-
params: ['_value', '_refs'],
1027-
body: {
1028-
type: NodeTypes.JS_BLOCK_STATEMENT,
1029-
body: [
1030-
{
1031-
content: `_refs['input'] = _value`
1032-
},
1033-
{
1034-
content: '_isRef(input) && (input.value = _value)'
1035-
}
1036-
]
1037-
}
967+
content: 'input',
968+
isStatic: false
1038969
}
1039970
}
1040971
]
1041972
})
1042973
})
1043974

1044-
test('the binding not exists (inline let ref input)', () => {
975+
test('script setup inline mode template ref (binding does not exist)', () => {
1045976
const { node } = parseWithElementTransform(`<input ref="input"/>`, {
1046-
inline: true,
1047-
bindingMetadata: {
1048-
input: BindingTypes.SETUP_LET
1049-
}
977+
inline: true
1050978
})
1051979
expect(node.props).toMatchObject({
1052980
type: NodeTypes.JS_OBJECT_EXPRESSION,
1053981
properties: [
1054982
{
1055983
type: NodeTypes.JS_PROPERTY,
1056984
key: {
1057-
type: NodeTypes.SIMPLE_EXPRESSION,
1058985
content: 'ref',
1059986
isStatic: true
1060987
},
1061988
value: {
1062-
type: NodeTypes.JS_FUNCTION_EXPRESSION,
1063-
params: ['_value', '_refs'],
1064-
body: {
1065-
type: NodeTypes.JS_BLOCK_STATEMENT,
1066-
body: [
1067-
{
1068-
content: `_refs['input'] = _value`
1069-
},
1070-
{
1071-
content:
1072-
'_isRef(input) ? input.value = _value : input = _value'
1073-
}
1074-
]
1075-
}
989+
content: 'input',
990+
isStatic: true
1076991
}
1077992
}
1078993
]

‎packages/compiler-core/src/compat/compatConfig.ts

-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export const enum CompilerDeprecationTypes {
2020
COMPILER_V_BIND_OBJECT_ORDER = 'COMPILER_V_BIND_OBJECT_ORDER',
2121
COMPILER_V_ON_NATIVE = 'COMPILER_V_ON_NATIVE',
2222
COMPILER_V_IF_V_FOR_PRECEDENCE = 'COMPILER_V_IF_V_FOR_PRECEDENCE',
23-
COMPILER_V_FOR_REF = 'COMPILER_V_FOR_REF',
2423
COMPILER_NATIVE_TEMPLATE = 'COMPILER_NATIVE_TEMPLATE',
2524
COMPILER_INLINE_TEMPLATE = 'COMPILER_INLINE_TEMPLATE',
2625
COMPILER_FILTERS = 'COMPILER_FILTER'
@@ -79,13 +78,6 @@ const deprecationData: Record<CompilerDeprecationTypes, DeprecationData> = {
7978
link: `https://v3.vuejs.org/guide/migration/v-if-v-for.html`
8079
},
8180

82-
[CompilerDeprecationTypes.COMPILER_V_FOR_REF]: {
83-
message:
84-
`Ref usage on v-for no longer creates array ref values in Vue 3. ` +
85-
`Consider using function refs or refactor to avoid ref usage altogether.`,
86-
link: `https://v3.vuejs.org/guide/migration/array-refs.html`
87-
},
88-
8981
[CompilerDeprecationTypes.COMPILER_NATIVE_TEMPLATE]: {
9082
message:
9183
`<template> with no special directives will render as a native template ` +

‎packages/compiler-core/src/transforms/transformElement.ts

+37-62
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ import {
1919
TemplateTextChildNode,
2020
DirectiveArguments,
2121
createVNodeCall,
22-
ConstantTypes,
23-
JSChildNode,
24-
createFunctionExpression,
25-
createBlockStatement
22+
ConstantTypes
2623
} from '../ast'
2724
import {
2825
PatchFlags,
@@ -48,8 +45,7 @@ import {
4845
KEEP_ALIVE,
4946
SUSPENSE,
5047
UNREF,
51-
GUARD_REACTIVE_PROPS,
52-
IS_REF
48+
GUARD_REACTIVE_PROPS
5349
} from '../runtimeHelpers'
5450
import {
5551
getInnerRange,
@@ -467,20 +463,32 @@ export function buildProps(
467463
const prop = props[i]
468464
if (prop.type === NodeTypes.ATTRIBUTE) {
469465
const { loc, name, value } = prop
470-
let valueNode = createSimpleExpression(
471-
value ? value.content : '',
472-
true,
473-
value ? value.loc : loc
474-
) as JSChildNode
466+
let isStatic = true
475467
if (name === 'ref') {
476468
hasRef = true
469+
if (context.scopes.vFor > 0) {
470+
properties.push(
471+
createObjectProperty(
472+
createSimpleExpression('ref_for', true),
473+
createSimpleExpression('true')
474+
)
475+
)
476+
}
477477
// in inline mode there is no setupState object, so we can't use string
478478
// keys to set the ref. Instead, we need to transform it to pass the
479479
// actual ref instead.
480-
if (!__BROWSER__ && context.inline && value?.content) {
481-
valueNode = createFunctionExpression(['_value', '_refs'])
482-
valueNode.body = createBlockStatement(
483-
processInlineRef(context, value.content)
480+
if (
481+
!__BROWSER__ &&
482+
value &&
483+
context.inline &&
484+
context.bindingMetadata[value.content]
485+
) {
486+
isStatic = false
487+
properties.push(
488+
createObjectProperty(
489+
createSimpleExpression('ref_key', true),
490+
createSimpleExpression(value.content, true, value.loc)
491+
)
484492
)
485493
}
486494
}
@@ -504,7 +512,11 @@ export function buildProps(
504512
true,
505513
getInnerRange(loc, 0, name.length)
506514
),
507-
valueNode
515+
createSimpleExpression(
516+
value ? value.content : '',
517+
isStatic,
518+
value ? value.loc : loc
519+
)
508520
)
509521
)
510522
} else {
@@ -555,6 +567,15 @@ export function buildProps(
555567
shouldUseBlock = true
556568
}
557569

570+
if (isVBind && isStaticArgOf(arg, 'ref') && context.scopes.vFor > 0) {
571+
properties.push(
572+
createObjectProperty(
573+
createSimpleExpression('ref_for', true),
574+
createSimpleExpression('true')
575+
)
576+
)
577+
}
578+
558579
// special case for v-bind and v-on with no argument
559580
if (!arg && (isVBind || isVOn)) {
560581
hasDynamicKeys = true
@@ -654,25 +675,6 @@ export function buildProps(
654675
}
655676
}
656677
}
657-
658-
if (
659-
__COMPAT__ &&
660-
prop.type === NodeTypes.ATTRIBUTE &&
661-
prop.name === 'ref' &&
662-
context.scopes.vFor > 0 &&
663-
checkCompatEnabled(
664-
CompilerDeprecationTypes.COMPILER_V_FOR_REF,
665-
context,
666-
prop.loc
667-
)
668-
) {
669-
properties.push(
670-
createObjectProperty(
671-
createSimpleExpression('refInFor', true),
672-
createSimpleExpression('true', false)
673-
)
674-
)
675-
}
676678
}
677679

678680
let propsExpression: PropsExpression | undefined = undefined
@@ -914,30 +916,3 @@ function stringifyDynamicPropNames(props: string[]): string {
914916
function isComponentTag(tag: string) {
915917
return tag === 'component' || tag === 'Component'
916918
}
917-
918-
function processInlineRef(
919-
context: TransformContext,
920-
raw: string
921-
): JSChildNode[] {
922-
const body = [createSimpleExpression(`_refs['${raw}'] = _value`)]
923-
const { bindingMetadata, helperString } = context
924-
const type = bindingMetadata[raw]
925-
if (type === BindingTypes.SETUP_REF) {
926-
body.push(createSimpleExpression(`${raw}.value = _value`))
927-
} else if (type === BindingTypes.SETUP_MAYBE_REF) {
928-
body.push(
929-
createSimpleExpression(
930-
`${helperString(IS_REF)}(${raw}) && (${raw}.value = _value)`
931-
)
932-
)
933-
} else if (type === BindingTypes.SETUP_LET) {
934-
body.push(
935-
createSimpleExpression(
936-
`${helperString(
937-
IS_REF
938-
)}(${raw}) ? ${raw}.value = _value : ${raw} = _value`
939-
)
940-
)
941-
}
942-
return body
943-
}

‎packages/runtime-core/__tests__/apiTemplateRef.spec.ts

+77
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,81 @@ describe('api: template refs', () => {
365365
expect(elRef1.value).toBeNull()
366366
expect(elRef1.value).toBe(elRef2.value)
367367
})
368+
369+
// compiled output of <script setup> inline mode
370+
test('raw ref with ref_key', () => {
371+
let refs: any
372+
373+
const el = ref()
374+
375+
const App = {
376+
mounted() {
377+
refs = (this as any).$refs
378+
},
379+
render() {
380+
return h(
381+
'div',
382+
{
383+
ref: el,
384+
ref_key: 'el'
385+
},
386+
'hello'
387+
)
388+
}
389+
}
390+
const root = nodeOps.createElement('div')
391+
render(h(App), root)
392+
393+
expect(serializeInner(el.value)).toBe('hello')
394+
expect(serializeInner(refs.el)).toBe('hello')
395+
})
396+
397+
// compiled output of v-for + template ref
398+
test('ref in v-for', async () => {
399+
const show = ref(true)
400+
const list = reactive([1, 2, 3])
401+
const listRefs = ref([])
402+
const mapRefs = () => listRefs.value.map(n => serializeInner(n))
403+
404+
const App = {
405+
render() {
406+
return show.value
407+
? h(
408+
'ul',
409+
list.map(i =>
410+
h(
411+
'li',
412+
{
413+
ref: listRefs,
414+
ref_for: true
415+
},
416+
i
417+
)
418+
)
419+
)
420+
: null
421+
}
422+
}
423+
const root = nodeOps.createElement('div')
424+
render(h(App), root)
425+
426+
expect(mapRefs()).toMatchObject(['1', '2', '3'])
427+
428+
list.push(4)
429+
await nextTick()
430+
expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
431+
432+
list.shift()
433+
await nextTick()
434+
expect(mapRefs()).toMatchObject(['2', '3', '4'])
435+
436+
show.value = !show.value
437+
await nextTick()
438+
439+
expect(mapRefs()).toMatchObject([])
440+
441+
show.value = !show.value
442+
await nextTick()
443+
expect(mapRefs()).toMatchObject(['2', '3', '4'])
444+
})
368445
})

‎packages/runtime-core/__tests__/vnode.spec.ts

+35-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from '../src/vnode'
1313
import { Data } from '../src/component'
1414
import { ShapeFlags, PatchFlags } from '@vue/shared'
15-
import { h, reactive, isReactive, setBlockTracking } from '../src'
15+
import { h, reactive, isReactive, setBlockTracking, ref } from '../src'
1616
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
1717
import { setCurrentRenderingInstance } from '../src/componentRenderContext'
1818

@@ -236,37 +236,60 @@ describe('vnode', () => {
236236

237237
setCurrentRenderingInstance(mockInstance1)
238238
const original = createVNode('div', { ref: 'foo' })
239-
expect(original.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })
239+
expect(original.ref).toMatchObject({
240+
i: mockInstance1,
241+
r: 'foo',
242+
f: false
243+
})
240244

241245
// clone and preserve original ref
242246
const cloned1 = cloneVNode(original)
243-
expect(cloned1.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })
247+
expect(cloned1.ref).toMatchObject({ i: mockInstance1, r: 'foo', f: false })
244248

245249
// cloning with new ref, but with same context instance
246250
const cloned2 = cloneVNode(original, { ref: 'bar' })
247-
expect(cloned2.ref).toStrictEqual({ i: mockInstance1, r: 'bar' })
251+
expect(cloned2.ref).toMatchObject({ i: mockInstance1, r: 'bar', f: false })
248252

249253
// cloning and adding ref to original that has no ref
250254
const original2 = createVNode('div')
251255
const cloned3 = cloneVNode(original2, { ref: 'bar' })
252-
expect(cloned3.ref).toStrictEqual({ i: mockInstance1, r: 'bar' })
256+
expect(cloned3.ref).toMatchObject({ i: mockInstance1, r: 'bar', f: false })
253257

254258
// cloning with different context instance
255259
setCurrentRenderingInstance(mockInstance2)
256260

257261
// clone and preserve original ref
258262
const cloned4 = cloneVNode(original)
259263
// #1311 should preserve original context instance!
260-
expect(cloned4.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })
264+
expect(cloned4.ref).toMatchObject({ i: mockInstance1, r: 'foo', f: false })
261265

262266
// cloning with new ref, but with same context instance
263267
const cloned5 = cloneVNode(original, { ref: 'bar' })
264268
// new ref should use current context instance and overwrite original
265-
expect(cloned5.ref).toStrictEqual({ i: mockInstance2, r: 'bar' })
269+
expect(cloned5.ref).toMatchObject({ i: mockInstance2, r: 'bar', f: false })
266270

267271
// cloning and adding ref to original that has no ref
268272
const cloned6 = cloneVNode(original2, { ref: 'bar' })
269-
expect(cloned6.ref).toStrictEqual({ i: mockInstance2, r: 'bar' })
273+
expect(cloned6.ref).toMatchObject({ i: mockInstance2, r: 'bar', f: false })
274+
275+
const original3 = createVNode('div', { ref: 'foo', ref_for: true })
276+
expect(original3.ref).toMatchObject({
277+
i: mockInstance2,
278+
r: 'foo',
279+
f: true
280+
})
281+
const cloned7 = cloneVNode(original3, { ref: 'bar', ref_for: true })
282+
expect(cloned7.ref).toMatchObject({ i: mockInstance2, r: 'bar', f: true })
283+
284+
const r = ref()
285+
const original4 = createVNode('div', { ref: r, ref_key: 'foo' })
286+
expect(original4.ref).toMatchObject({
287+
i: mockInstance2,
288+
r,
289+
k: 'foo'
290+
})
291+
const cloned8 = cloneVNode(original4)
292+
expect(cloned8.ref).toMatchObject({ i: mockInstance2, r, k: 'foo' })
270293

271294
setCurrentRenderingInstance(null)
272295
})
@@ -277,14 +300,14 @@ describe('vnode', () => {
277300

278301
setCurrentRenderingInstance(mockInstance1)
279302
const original = createVNode('div', { ref: 'foo' })
280-
expect(original.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })
303+
expect(original.ref).toMatchObject({ i: mockInstance1, r: 'foo', f: false })
281304

282305
// clone and preserve original ref
283306
setCurrentRenderingInstance(mockInstance2)
284307
const cloned1 = cloneVNode(original, { ref: 'bar' }, true)
285-
expect(cloned1.ref).toStrictEqual([
286-
{ i: mockInstance1, r: 'foo' },
287-
{ i: mockInstance2, r: 'bar' }
308+
expect(cloned1.ref).toMatchObject([
309+
{ i: mockInstance1, r: 'foo', f: false },
310+
{ i: mockInstance2, r: 'bar', f: false }
288311
])
289312

290313
setCurrentRenderingInstance(null)

‎packages/runtime-core/src/compat/compatConfig.ts

-8
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export const enum DeprecationTypes {
4646
WATCH_ARRAY = 'WATCH_ARRAY',
4747
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
4848

49-
V_FOR_REF = 'V_FOR_REF',
5049
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
5150
CUSTOM_DIR = 'CUSTOM_DIR',
5251

@@ -298,13 +297,6 @@ export const deprecationData: Record<DeprecationTypes, DeprecationData> = {
298297
link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
299298
},
300299

301-
[DeprecationTypes.V_FOR_REF]: {
302-
message:
303-
`Ref usage on v-for no longer creates array ref values in Vue 3. ` +
304-
`Consider using function refs or refactor to avoid ref usage altogether.`,
305-
link: `https://v3.vuejs.org/guide/migration/array-refs.html`
306-
},
307-
308300
[DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
309301
message:
310302
`Using keyCode as v-on modifier is no longer supported. ` +

‎packages/runtime-core/src/compat/ref.ts

-45
This file was deleted.

‎packages/runtime-core/src/renderer.ts

+46-33
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
hasOwn,
4141
invokeArrayFns,
4242
isArray,
43-
getGlobalThis
43+
getGlobalThis,
44+
remove
4445
} from '@vue/shared'
4546
import {
4647
queueJob,
@@ -86,7 +87,6 @@ import { initFeatureFlags } from './featureFlags'
8687
import { isAsyncWrapper } from './apiAsyncComponent'
8788
import { isCompatEnabled } from './compat/compatConfig'
8889
import { DeprecationTypes } from './compat/compatConfig'
89-
import { registerLegacyRef } from './compat/ref'
9090

9191
export interface Renderer<HostElement = RendererElement> {
9292
render: RootRenderFunction<HostElement>
@@ -2407,40 +2407,53 @@ export function setRef(
24072407
}
24082408
}
24092409

2410-
if (isString(ref)) {
2411-
const doSet = () => {
2412-
if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
2413-
registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
2414-
} else {
2415-
refs[ref] = value
2410+
if (isFunction(ref)) {
2411+
callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
2412+
} else {
2413+
const _isString = isString(ref)
2414+
const _isRef = isRef(ref)
2415+
if (_isString || _isRef) {
2416+
const doSet = () => {
2417+
if (rawRef.f) {
2418+
const existing = _isString ? refs[ref] : ref.value
2419+
if (isUnmount) {
2420+
isArray(existing) && remove(existing, refValue)
2421+
} else {
2422+
if (!isArray(existing)) {
2423+
if (_isString) {
2424+
refs[ref] = [refValue]
2425+
} else {
2426+
ref.value = [refValue]
2427+
if (rawRef.k) refs[rawRef.k] = ref.value
2428+
}
2429+
} else if (!existing.includes(refValue)) {
2430+
existing.push(refValue)
2431+
}
2432+
}
2433+
} else if (_isString) {
2434+
refs[ref] = value
2435+
if (hasOwn(setupState, ref)) {
2436+
setupState[ref] = value
2437+
}
2438+
} else if (isRef(ref)) {
2439+
ref.value = value
2440+
if (rawRef.k) refs[rawRef.k] = value
2441+
} else if (__DEV__) {
2442+
warn('Invalid template ref type:', ref, `(${typeof ref})`)
2443+
}
24162444
}
2417-
if (hasOwn(setupState, ref)) {
2418-
setupState[ref] = value
2445+
if (value) {
2446+
// #1789: for non-null values, set them after render
2447+
// null values means this is unmount and it should not overwrite another
2448+
// ref with the same key
2449+
;(doSet as SchedulerJob).id = -1
2450+
queuePostRenderEffect(doSet, parentSuspense)
2451+
} else {
2452+
doSet()
24192453
}
2454+
} else if (__DEV__) {
2455+
warn('Invalid template ref type:', ref, `(${typeof ref})`)
24202456
}
2421-
// #1789: for non-null values, set them after render
2422-
// null values means this is unmount and it should not overwrite another
2423-
// ref with the same key
2424-
if (value) {
2425-
;(doSet as SchedulerJob).id = -1
2426-
queuePostRenderEffect(doSet, parentSuspense)
2427-
} else {
2428-
doSet()
2429-
}
2430-
} else if (isRef(ref)) {
2431-
const doSet = () => {
2432-
ref.value = value
2433-
}
2434-
if (value) {
2435-
;(doSet as SchedulerJob).id = -1
2436-
queuePostRenderEffect(doSet, parentSuspense)
2437-
} else {
2438-
doSet()
2439-
}
2440-
} else if (isFunction(ref)) {
2441-
callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
2442-
} else if (__DEV__) {
2443-
warn('Invalid template ref type:', value, `(${typeof value})`)
24442457
}
24452458
}
24462459

‎packages/runtime-core/src/vnode.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import { hmrDirtyComponents } from './hmr'
4242
import { convertLegacyComponent } from './compat/component'
4343
import { convertLegacyVModelProps } from './compat/componentVModel'
4444
import { defineLegacyVNodeProperties } from './compat/renderFn'
45-
import { convertLegacyRefInFor } from './compat/ref'
4645

4746
export const Fragment = Symbol(__DEV__ ? 'Fragment' : undefined) as any as {
4847
__isFragment: true
@@ -73,7 +72,8 @@ export type VNodeRef =
7372
export type VNodeNormalizedRefAtom = {
7473
i: ComponentInternalInstance
7574
r: VNodeRef
76-
f?: boolean // v2 compat only, refInFor marker
75+
k?: string // setup ref key
76+
f?: boolean // refInFor marker
7777
}
7878

7979
export type VNodeNormalizedRef =
@@ -92,6 +92,8 @@ export type VNodeHook =
9292
export type VNodeProps = {
9393
key?: string | number | symbol
9494
ref?: VNodeRef
95+
ref_for?: boolean
96+
ref_key?: string
9597

9698
// vnode hooks
9799
onVnodeBeforeMount?: VNodeMountHook | VNodeMountHook[]
@@ -380,11 +382,15 @@ export const InternalObjectKey = `__vInternal`
380382
const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
381383
key != null ? key : null
382384

383-
const normalizeRef = ({ ref }: VNodeProps): VNodeNormalizedRefAtom | null => {
385+
const normalizeRef = ({
386+
ref,
387+
ref_key,
388+
ref_for
389+
}: VNodeProps): VNodeNormalizedRefAtom | null => {
384390
return (
385391
ref != null
386392
? isString(ref) || isRef(ref) || isFunction(ref)
387-
? { i: currentRenderingInstance, r: ref }
393+
? { i: currentRenderingInstance, r: ref, k: ref_key, f: !!ref_for }
388394
: ref
389395
: null
390396
) as any
@@ -468,7 +474,6 @@ function createBaseVNode(
468474

469475
if (__COMPAT__) {
470476
convertLegacyVModelProps(vnode)
471-
convertLegacyRefInFor(vnode)
472477
defineLegacyVNodeProperties(vnode)
473478
}
474479

‎packages/shared/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export const isIntegerKey = (key: unknown) =>
8383

8484
export const isReservedProp = /*#__PURE__*/ makeMap(
8585
// the leading comma is intentional so empty string "" is also included
86-
',key,ref,' +
86+
',key,ref,ref_for,ref_key,' +
8787
'onVnodeBeforeMount,onVnodeMounted,' +
8888
'onVnodeBeforeUpdate,onVnodeUpdated,' +
8989
'onVnodeBeforeUnmount,onVnodeUnmounted'

‎packages/vue-compat/__tests__/refInfor.spec.ts

-57
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.