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

fix(patch): clone insert hooks to avoid being mutated during iteration (#12905)

xdm пре 2 година
родитељ
комит
c22363425a
2 измењених фајлова са 43 додато и 2 уклоњено
  1. 5 2
      src/core/vdom/patch.ts
  2. 38 0
      test/unit/modules/vdom/patch/edge-cases.spec.ts

+ 5 - 2
src/core/vdom/patch.ts

@@ -878,8 +878,11 @@ export function createPatchFunction(backend) {
               const insert = ancestor.data.hook.insert
               if (insert.merged) {
                 // start at index 1 to avoid re-invoking component mounted hook
-                for (let i = 1; i < insert.fns.length; i++) {
-                  insert.fns[i]()
+                // clone insert hooks to avoid being mutated during iteration.
+                // e.g. for customed directives under transition group.
+                const cloned = insert.fns.slice(1)
+                for (let i = 0; i < cloned.length; i++) {
+                  cloned[i]()
                 }
               }
             } else {

+ 38 - 0
test/unit/modules/vdom/patch/edge-cases.spec.ts

@@ -467,4 +467,42 @@ describe('vdom patch: edge cases', () => {
     vm.visible = false
     vm.$nextTick(done)
   })
+
+  it('should call directive\'s inserted hook correctly when template root is changed', done => {
+    let insertCalled = false
+    const vm = new Vue({
+      data: {
+        isOn: false
+      },
+      components: {
+        'v-switch': {
+          props: {
+            on: Boolean
+          },
+          template: `
+            <div v-if="on" key="on">On</div>
+            <div v-else key="off">Off</div>`
+        }
+      },
+      directives: {
+        foo: {
+          inserted() {
+            insertCalled = true
+          }
+        }
+      },
+      template: `
+        <transition-group>
+          <v-switch key="swicth" v-foo :on="isOn"/>
+        </transition-group>
+      `
+    }).$mount()
+
+    vm.isOn = true
+    insertCalled = false
+    waitForUpdate(() => {
+      expect(vm.$el.textContent).toMatch('On')
+      expect(insertCalled).toBe(true)
+    }).then(done)
+  })
 })