Skip to content

Commit a32870a

Browse files
authored
fix: inherit el for static nodes inside keyed template fragment (#2089)
fix #2080
1 parent 612eb67 commit a32870a

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

packages/runtime-core/__tests__/rendererFragment.spec.ts

+47-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import {
1010
dumpOps,
1111
NodeOpTypes,
1212
serializeInner,
13-
createTextVNode
13+
createTextVNode,
14+
createBlock,
15+
openBlock,
16+
createCommentVNode
1417
} from '@vue/runtime-test'
1518
import { PatchFlags } from '@vue/shared'
19+
import { renderList } from '../src/helpers/renderList'
1620

1721
describe('renderer: fragment', () => {
1822
it('should allow returning multiple component root nodes', () => {
@@ -269,4 +273,46 @@ describe('renderer: fragment', () => {
269273
render(null, root)
270274
expect(serializeInner(root)).toBe(``)
271275
})
276+
277+
// #2080
278+
test('`template` keyed fragment w/ comment + hoisted node', () => {
279+
const root = nodeOps.createElement('div')
280+
const hoisted = h('span')
281+
282+
const renderFn = (items: string[]) => {
283+
return (
284+
openBlock(true),
285+
createBlock(
286+
Fragment,
287+
null,
288+
renderList(items, item => {
289+
return (
290+
openBlock(),
291+
createBlock(
292+
Fragment,
293+
{ key: item },
294+
[
295+
createCommentVNode('comment'),
296+
hoisted,
297+
createVNode('div', null, item, PatchFlags.TEXT)
298+
],
299+
PatchFlags.STABLE_FRAGMENT
300+
)
301+
)
302+
}),
303+
PatchFlags.KEYED_FRAGMENT
304+
)
305+
)
306+
}
307+
308+
render(renderFn(['one', 'two']), root)
309+
expect(serializeInner(root)).toBe(
310+
`<!--comment--><span></span><div>one</div><!--comment--><span></span><div>two</div>`
311+
)
312+
313+
render(renderFn(['two', 'one']), root)
314+
expect(serializeInner(root)).toBe(
315+
`<!--comment--><span></span><div>two</div><!--comment--><span></span><div>one</div>`
316+
)
317+
})
272318
})

packages/runtime-core/src/renderer.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -1153,8 +1153,10 @@ function baseCreateRenderer(
11531153
parentSuspense,
11541154
isSVG
11551155
)
1156-
if (__DEV__ && parentComponent && parentComponent.type.__hmrId) {
1157-
traverseStaticChildren(n1, n2)
1156+
// #2080 if the stable fragment has a key, it's a <template v-for> that may
1157+
// get moved around. Make sure all root level vnodes inherit el.
1158+
if (n2.key != null) {
1159+
traverseStaticChildren(n1, n2, true /* shallow */)
11581160
}
11591161
} else {
11601162
// keyed / unkeyed, or manual fragments.
@@ -2166,9 +2168,12 @@ function baseCreateRenderer(
21662168
* inside a block also inherit the DOM element from the previous tree so that
21672169
* HMR updates (which are full updates) can retrieve the element for patching.
21682170
*
2169-
* Dev only.
2171+
* #2080
2172+
* Inside keyed `template` fragment static children, if a fragment is moved,
2173+
* the children will always moved so that need inherit el form previous nodes
2174+
* to ensure correct moved position.
21702175
*/
2171-
const traverseStaticChildren = (n1: VNode, n2: VNode) => {
2176+
const traverseStaticChildren = (n1: VNode, n2: VNode, shallow = false) => {
21722177
const ch1 = n1.children
21732178
const ch2 = n2.children
21742179
if (isArray(ch1) && isArray(ch2)) {
@@ -2181,7 +2186,10 @@ function baseCreateRenderer(
21812186
if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.HYDRATE_EVENTS) {
21822187
c2.el = c1.el
21832188
}
2184-
traverseStaticChildren(c1, c2)
2189+
if (!shallow) traverseStaticChildren(c1, c2)
2190+
}
2191+
if (__DEV__ && c2.type === Comment) {
2192+
c2.el = c1.el
21852193
}
21862194
}
21872195
}

0 commit comments

Comments
 (0)