]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix cases of reused children arrays in render functions (#3670)
authorHcySunYang <HcySunYang@outlook.com>
Thu, 27 May 2021 21:06:55 +0000 (05:06 +0800)
committerGitHub <noreply@github.com>
Thu, 27 May 2021 21:06:55 +0000 (17:06 -0400)
fix #3666

packages/runtime-core/__tests__/rendererComponent.spec.ts
packages/runtime-core/src/vnode.ts

index 02070d384e9a06a6db480f815738a491397ac979..fc004045fd9587743d7a706f475f3f12c4a22c4f 100644 (file)
@@ -296,4 +296,39 @@ describe('renderer: component', () => {
       expect(serializeInner(root)).toBe(`<h1>1</h1>`)
     })
   })
+
+  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(`<h1></h1><h1></h1><h1></h1>`)
+
+    render(null, root)
+    expect(serializeInner(root)).toBe(``)
+    expect(ids).toEqual([ids[0], ids[0] + 1, ids[0] + 2])
+  })
 })
index 7109287d011b86c837e05f156fdf9e8a0bf1bbf9..da99f2ece3178d8da3da27f02da007f881d1c658 100644 (file)
@@ -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))