diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vMemo.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vMemo.spec.ts.snap index 220bc177418..86e0b3d2fd5 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vMemo.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vMemo.spec.ts.snap @@ -1,5 +1,23 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`compiler: v-memo transform > element v-for key expression prefixing + v-memo 1`] = ` +"import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, isMemoSame as _isMemoSame, withMemo as _withMemo } from "vue" + +export function render(_ctx, _cache) { + return (_openBlock(), _createElementBlock("div", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tableData, (data, __, ___, _cached) => { + const _memo = (_ctx.getLetter(data)) + if (_cached && _cached.key === _ctx.getId(data) && _isMemoSame(_cached, _memo)) return _cached + const _item = (_openBlock(), _createElementBlock("span", { + key: _ctx.getId(data) + })) + _item.memo = _memo + return _item + }, _cache, 0), 128 /* KEYED_FRAGMENT */)) + ])) +}" +`; + exports[`compiler: v-memo transform > on component 1`] = ` "import { resolveComponent as _resolveComponent, createVNode as _createVNode, withMemo as _withMemo, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue" diff --git a/packages/compiler-core/__tests__/transforms/vMemo.spec.ts b/packages/compiler-core/__tests__/transforms/vMemo.spec.ts index 85769e6e977..41e7d922ebe 100644 --- a/packages/compiler-core/__tests__/transforms/vMemo.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vMemo.spec.ts @@ -53,4 +53,12 @@ describe('compiler: v-memo transform', () => { ), ).toMatchSnapshot() }) + + test('element v-for key expression prefixing + v-memo', () => { + expect( + compile( + ``, + ), + ).toMatchSnapshot() + }) }) diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts index ec2d4685314..9ae8897e674 100644 --- a/packages/compiler-core/src/transforms/transformExpression.ts +++ b/packages/compiler-core/src/transforms/transformExpression.ts @@ -24,7 +24,7 @@ import { isStaticPropertyKey, walkIdentifiers, } from '../babelUtils' -import { advancePositionWithClone, isSimpleIdentifier } from '../utils' +import { advancePositionWithClone, findDir, isSimpleIdentifier } from '../utils' import { genPropsAccessExp, hasOwn, @@ -54,6 +54,7 @@ export const transformExpression: NodeTransform = (node, context) => { ) } else if (node.type === NodeTypes.ELEMENT) { // handle directives on element + const memo = findDir(node, 'memo') for (let i = 0; i < node.props.length; i++) { const dir = node.props[i] // do not process for v-on & v-for since they are special handled @@ -65,7 +66,14 @@ export const transformExpression: NodeTransform = (node, context) => { if ( exp && exp.type === NodeTypes.SIMPLE_EXPRESSION && - !(dir.name === 'on' && arg) + !(dir.name === 'on' && arg) && + // key has been processed in transformFor(vMemo + vFor) + !( + memo && + arg && + arg.type === NodeTypes.SIMPLE_EXPRESSION && + arg.content === 'key' + ) ) { dir.exp = processExpression( exp, diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index ec1c21ff88b..0dca0ba9ab4 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -63,17 +63,27 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform( const isTemplate = isTemplateNode(node) const memo = findDir(node, 'memo') const keyProp = findProp(node, `key`, false, true) - if (keyProp && keyProp.type === NodeTypes.DIRECTIVE && !keyProp.exp) { + const isDirKey = keyProp && keyProp.type === NodeTypes.DIRECTIVE + if (isDirKey && !keyProp.exp) { // resolve :key shorthand #10882 transformBindShorthand(keyProp, context) } - const keyExp = + let keyExp = keyProp && (keyProp.type === NodeTypes.ATTRIBUTE ? keyProp.value ? createSimpleExpression(keyProp.value.content, true) : undefined : keyProp.exp) + + if (memo && keyExp && isDirKey) { + if (!__BROWSER__) { + keyProp.exp = keyExp = processExpression( + keyExp as SimpleExpressionNode, + context, + ) + } + } const keyProperty = keyProp && keyExp ? createObjectProperty(`key`, keyExp) : null