Преглед изворни кода

fix(core): avoid mutating original children when cloning vnode

The on-demand clone strategy introduced in 956756b mutates the owner
array of the cloned vnode. This causes the newly cloned vnode to be
destroyed when the parent node is destroyed. This is fixed by cloning
the children array when cloning a vnode.

fix #7975
Evan You пре 7 година
родитељ
комит
097f6229df
2 измењених фајлова са 58 додато и 1 уклоњено
  1. 4 1
      src/core/vdom/vnode.js
  2. 54 0
      test/unit/features/component/component-slot.spec.js

+ 4 - 1
src/core/vdom/vnode.js

@@ -90,7 +90,10 @@ export function cloneVNode (vnode: VNode): VNode {
   const cloned = new VNode(
     vnode.tag,
     vnode.data,
-    vnode.children,
+    // #7975
+    // clone children array to avoid mutating original in case of cloning
+    // a child.
+    vnode.children && vnode.children.slice(),
     vnode.text,
     vnode.elm,
     vnode.context,

+ 54 - 0
test/unit/features/component/component-slot.spec.js

@@ -886,4 +886,58 @@ describe('Component slot', () => {
       expect(vm.$el.textContent).toBe('foo')
     }).then(done)
   })
+
+  // #7975
+  it('should update named slot correctly when its position in the tree changed', done => {
+    const ChildComponent = {
+      template: '<b>{{ message }}</b>',
+      props: ['message']
+    }
+     let parentVm
+    const ParentComponent = {
+      template: `
+        <div>
+          <span v-if="alter">
+            <span><slot name="foo" /></span>
+          </span>
+          <span v-else>
+            <slot name="foo" />
+          </span>
+        </div>
+      `,
+      data () {
+        return {
+          alter: true
+        }
+      },
+      mounted () {
+        parentVm = this
+      }
+    }
+     const vm = new Vue({
+      template: `
+        <parent-component>
+          <span slot="foo">
+            <child-component :message="message" />
+          </span>
+        </parent-component>
+      `,
+      components: {
+        ChildComponent,
+        ParentComponent
+      },
+      data () {
+        return {
+          message: 1
+        }
+      }
+    }).$mount()
+     expect(vm.$el.firstChild.innerHTML).toBe('<span><span><b>1</b></span></span>')
+    parentVm.alter = false
+    waitForUpdate(() => {
+      vm.message = 2
+    }).then(() => {
+      expect(vm.$el.firstChild.innerHTML).toBe('<span><b>2</b></span>')
+    }).then(done)
+  })
 })