diff --git a/src/core/vdom/patch.ts b/src/core/vdom/patch.ts index d0594863e3b..173840787bc 100644 --- a/src/core/vdom/patch.ts +++ b/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 { diff --git a/test/unit/modules/vdom/patch/edge-cases.spec.ts b/test/unit/modules/vdom/patch/edge-cases.spec.ts index f9295533b1e..1b27f2b630c 100644 --- a/test/unit/modules/vdom/patch/edge-cases.spec.ts +++ b/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: ` +
On
+
Off
` + } + }, + directives: { + foo: { + inserted() { + insertCalled = true + } + } + }, + template: ` + + + + ` + }).$mount() + + vm.isOn = true + insertCalled = false + waitForUpdate(() => { + expect(vm.$el.textContent).toMatch('On') + expect(insertCalled).toBe(true) + }).then(done) + }) })