From: HcySunYang Date: Thu, 27 May 2021 21:06:55 +0000 (+0800) Subject: fix(runtime-core): fix cases of reused children arrays in render functions (#3670) X-Git-Tag: v3.1.0-beta.6~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a641eb201fe51620d50884b988f6fefc3e21a20b;p=thirdparty%2Fvuejs%2Fcore.git fix(runtime-core): fix cases of reused children arrays in render functions (#3670) fix #3666 --- diff --git a/packages/runtime-core/__tests__/rendererComponent.spec.ts b/packages/runtime-core/__tests__/rendererComponent.spec.ts index 02070d384e..fc004045fd 100644 --- a/packages/runtime-core/__tests__/rendererComponent.spec.ts +++ b/packages/runtime-core/__tests__/rendererComponent.spec.ts @@ -296,4 +296,39 @@ describe('renderer: component', () => { expect(serializeInner(root)).toBe(`

1

`) }) }) + + test('the component VNode should be cloned when reusing it', () => { + const Child = { + setup(props: any, { slots }: SetupContext) { + return () => { + const c = slots.default!() + return [c, c, c] + } + } + } + + const ids: number[] = [] + const Comp = { + render: () => h('h1'), + beforeUnmount() { + ids.push((this as any).$.uid) + } + } + + const App = { + setup() { + return () => { + return h(Child, () => [h(Comp)]) + } + } + } + + const root = nodeOps.createElement('div') + render(h(App), root) + expect(serializeInner(root)).toBe(`

`) + + render(null, root) + expect(serializeInner(root)).toBe(``) + expect(ids).toEqual([ids[0], ids[0] + 1, ids[0] + 2]) + }) }) diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 7109287d01..da99f2ece3 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -603,11 +603,16 @@ export function normalizeVNode(child: VNodeChild): VNode { return createVNode(Comment) } else if (isArray(child)) { // fragment - return createVNode(Fragment, null, child) + return createVNode( + Fragment, + null, + // #3666, avoid reference pollution when reusing vnode + child.slice() + ) } else if (typeof child === 'object') { // already vnode, this should be the most common since compiled templates // always produce all-vnode children arrays - return child.el === null ? child : cloneVNode(child) + return cloneIfMounted(child) } else { // strings and numbers return createVNode(Text, null, String(child))