diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 9e1f87aec8e..93140f13f4e 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -10,9 +10,13 @@ import { dumpOps, NodeOpTypes, serializeInner, - createTextVNode + createTextVNode, + createBlock, + openBlock, + createCommentVNode } from '@vue/runtime-test' import { PatchFlags } from '@vue/shared' +import { renderList } from '../src/helpers/renderList' describe('renderer: fragment', () => { it('should allow returning multiple component root nodes', () => { @@ -269,4 +273,46 @@ describe('renderer: fragment', () => { render(null, root) expect(serializeInner(root)).toBe(``) }) + + // #2080 + test('`template` keyed fragment w/ comment + hoisted node', () => { + const root = nodeOps.createElement('div') + const hoisted = h('span') + + const renderFn = (items: string[]) => { + return ( + openBlock(true), + createBlock( + Fragment, + null, + renderList(items, item => { + return ( + openBlock(), + createBlock( + Fragment, + { key: item }, + [ + createCommentVNode('comment'), + hoisted, + createVNode('div', null, item, PatchFlags.TEXT) + ], + PatchFlags.STABLE_FRAGMENT + ) + ) + }), + PatchFlags.KEYED_FRAGMENT + ) + ) + } + + render(renderFn(['one', 'two']), root) + expect(serializeInner(root)).toBe( + `
one
two
` + ) + + render(renderFn(['two', 'one']), root) + expect(serializeInner(root)).toBe( + `
two
one
` + ) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index a4cf97dc807..76ff20667eb 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1139,9 +1139,7 @@ function baseCreateRenderer( parentSuspense, isSVG ) - if (__DEV__ && parentComponent && parentComponent.type.__hmrId) { - traverseStaticChildren(n1, n2) - } + traverseStaticChildren(n1, n2) } else { // keyed / unkeyed, or manual fragments. // for keyed & unkeyed, since they are compiler generated from v-for, @@ -2152,7 +2150,10 @@ function baseCreateRenderer( * inside a block also inherit the DOM element from the previous tree so that * HMR updates (which are full updates) can retrieve the element for patching. * - * Dev only. + * #2080 + * Inside keyed `template` fragment static children, if a fragment is moved, + * the children will always moved so that need inherit el form previous nodes + * to ensure correct moved position. */ const traverseStaticChildren = (n1: VNode, n2: VNode) => { const ch1 = n1.children @@ -2169,6 +2170,9 @@ function baseCreateRenderer( } traverseStaticChildren(c1, c2) } + if (c2.type === Comment) { + c2.el = c1.el + } } } }