diff --git a/src/platforms/web/runtime/components/transition.js b/src/platforms/web/runtime/components/transition.js index b94a16c97..fb05b692e 100644 --- a/src/platforms/web/runtime/components/transition.js +++ b/src/platforms/web/runtime/components/transition.js @@ -2,7 +2,7 @@ // supports transition mode (out-in / in-out) import { warn } from 'core/util/index' -import { noop, camelize } from 'shared/util' +import { camelize } from 'shared/util' import { getRealChild, mergeVNodeHook } from 'core/vdom/helpers' export const transitionProps = { @@ -110,16 +110,11 @@ export default { } else if (mode === 'in-out') { let delayedLeave const performLeave = () => { delayedLeave() } - mergeVNodeHook(data, 'afterEnter', performLeave) mergeVNodeHook(data, 'enterCancelled', performLeave) - mergeVNodeHook(oldData, 'delayLeave', leave => { delayedLeave = leave }) - mergeVNodeHook(oldData, 'leaveCancelled', () => { - delayedLeave = noop - }) } } diff --git a/src/platforms/web/runtime/modules/transition.js b/src/platforms/web/runtime/modules/transition.js index 1d846097f..566f22010 100644 --- a/src/platforms/web/runtime/modules/transition.js +++ b/src/platforms/web/runtime/modules/transition.js @@ -160,6 +160,10 @@ export function leave (vnode: VNodeWithData, rm: Function) { } function performLeave () { + // the delayed leave may have already been cancelled + if (cb.cancelled) { + return + } // record leaving element if (!vnode.data.show) { (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode diff --git a/test/unit/features/component/component-keep-alive.spec.js b/test/unit/features/component/component-keep-alive.spec.js index ec0d87745..7b5a176d0 100644 --- a/test/unit/features/component/component-keep-alive.spec.js +++ b/test/unit/features/component/component-keep-alive.spec.js @@ -260,5 +260,71 @@ describe('Component keep-alive', () => { assertHookCalls(two, [1, 1, 1, 1, 0]) }).then(done) }) + + it('dynamic components, in-out with early cancel', done => { + let next + const vm = new Vue({ + template: `
+ + + +
`, + data: { view: 'one' }, + components, + methods: { + afterEnter () { + next() + } + } + }).$mount(el) + expect(vm.$el.textContent).toBe('one') + vm.view = 'two' + waitForUpdate(() => { + expect(vm.$el.innerHTML).toBe( + '
one
' + + '
two
' + ) + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
one
' + + '
two
' + ) + // switch again before enter finishes, + // this cancels both enter and leave. + vm.view = 'one' + }).then(() => { + // 1. the pending leaving "one" should be removed instantly. + // 2. the entering "two" should be placed into its final state instantly. + // 3. a new "one" is created and entering + expect(vm.$el.innerHTML).toBe( + '
two
' + + '
one
' + ) + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + + '
one
' + ) + }).thenWaitFor(_next => { next = _next }).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + + '
one
' + ) + }).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + + '
one
' + ) + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + + '
one
' + ) + }).thenWaitFor(duration + 10).then(() => { + expect(vm.$el.innerHTML).toBe( + '
one
' + ) + }).then(done).then(done) + }) } }) diff --git a/test/unit/features/transition/transition-mode.spec.js b/test/unit/features/transition/transition-mode.spec.js index e4cf148ec..c1fe15d59 100644 --- a/test/unit/features/transition/transition-mode.spec.js +++ b/test/unit/features/transition/transition-mode.spec.js @@ -142,7 +142,7 @@ if (!isIE9) { }).then(done) }) - it('dynamic components, in-out with leaveCancell', done => { + it('dynamic components, in-out with early cancel', done => { let next const vm = new Vue({ template: `