From b557d3fb8ae1e4e926c4ad0fbb2fa7abe50fd661 Mon Sep 17 00:00:00 2001 From: Tycho Date: Fri, 14 Jun 2024 17:10:13 +0800 Subject: [PATCH] fix(runtime-core): avoid traversing static children for vnodes w/ PatchFlags.BAIL (#11115) close #10547 --- .../__tests__/rendererFragment.spec.ts | 59 +++++++++++++++++++ packages/runtime-core/src/renderer.ts | 3 +- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 5f1e869366f..81cf7b8df31 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -3,6 +3,7 @@ import { NodeOpTypes, type TestElement, TestNodeTypes, + type VNode, createBlock, createCommentVNode, createTextVNode, @@ -316,6 +317,64 @@ describe('renderer: fragment', () => { ) }) + // #10547 + test('`template` fragment w/ render function', () => { + const renderFn = (vnode: VNode) => { + return ( + openBlock(), + createBlock( + Fragment, + null, + [createTextVNode('text'), (openBlock(), createBlock(vnode))], + PatchFlags.STABLE_FRAGMENT, + ) + ) + } + + const root = nodeOps.createElement('div') + const foo = h('div', ['foo']) + const bar = h('div', [h('div', 'bar')]) + + render(renderFn(foo), root) + expect(serializeInner(root)).toBe(`text
foo
`) + + render(renderFn(bar), root) + expect(serializeInner(root)).toBe(`text
bar
`) + + render(renderFn(foo), root) + expect(serializeInner(root)).toBe(`text
foo
`) + }) + + // #10547 + test('`template` fragment w/ render function + keyed vnode', () => { + const renderFn = (vnode: VNode) => { + return ( + openBlock(), + createBlock( + Fragment, + null, + [createTextVNode('text'), (openBlock(), createBlock(vnode))], + PatchFlags.STABLE_FRAGMENT, + ) + ) + } + + const root = nodeOps.createElement('div') + const foo = h('div', { key: 1 }, [h('div', 'foo')]) + const bar = h('div', { key: 2 }, [h('div', 'bar'), h('div', 'bar')]) + + render(renderFn(foo), root) + expect(serializeInner(root)).toBe(`text
foo
`) + + render(renderFn(bar), root) + expect(serializeInner(root)).toBe( + `text
bar
bar
`, + ) + + render(renderFn(foo), root) + expect(serializeInner(root)).toBe(`text
foo
`) + }) + // #6852 test('`template` keyed fragment w/ text', () => { const root = nodeOps.createElement('div') diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 2a35a5e1925..ccb89085c40 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2473,7 +2473,8 @@ export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) { c2 = ch2[i] = cloneIfMounted(ch2[i] as VNode) c2.el = c1.el } - if (!shallow) traverseStaticChildren(c1, c2) + if (!shallow && c2.patchFlag !== PatchFlags.BAIL) + traverseStaticChildren(c1, c2) } // #6852 also inherit for text nodes if (c2.type === Text) {