From a43d66743be2bd62b2398090663e41eeaf0dc75f Mon Sep 17 00:00:00 2001 From: JK Date: Wed, 30 Aug 2017 04:09:20 +0800 Subject: [PATCH] fix(transition): consider async placeholder as valid child to return (#6369) fix #6256 --- .../vdom/helpers/get-first-component-child.js | 3 +- src/core/vdom/helpers/index.js | 1 + src/core/vdom/helpers/is-async-placeholder.js | 5 ++ .../web/runtime/components/transition.js | 10 +-- .../component/component-keep-alive.spec.js | 84 +++++++++++++++++++ 5 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/core/vdom/helpers/is-async-placeholder.js diff --git a/src/core/vdom/helpers/get-first-component-child.js b/src/core/vdom/helpers/get-first-component-child.js index 70b2f199f94..f8649dd8728 100644 --- a/src/core/vdom/helpers/get-first-component-child.js +++ b/src/core/vdom/helpers/get-first-component-child.js @@ -1,12 +1,13 @@ /* @flow */ import { isDef } from 'shared/util' +import { isAsyncPlaceholder } from './is-async-placeholder' export function getFirstComponentChild (children: ?Array): ?VNode { if (Array.isArray(children)) { for (let i = 0; i < children.length; i++) { const c = children[i] - if (isDef(c) && isDef(c.componentOptions)) { + if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { return c } } diff --git a/src/core/vdom/helpers/index.js b/src/core/vdom/helpers/index.js index a1541e10544..320cf4c4343 100644 --- a/src/core/vdom/helpers/index.js +++ b/src/core/vdom/helpers/index.js @@ -6,3 +6,4 @@ export * from './update-listeners' export * from './normalize-children' export * from './resolve-async-component' export * from './get-first-component-child' +export * from './is-async-placeholder' diff --git a/src/core/vdom/helpers/is-async-placeholder.js b/src/core/vdom/helpers/is-async-placeholder.js new file mode 100644 index 00000000000..6c4a6596b6a --- /dev/null +++ b/src/core/vdom/helpers/is-async-placeholder.js @@ -0,0 +1,5 @@ +/* @flow */ + +export function isAsyncPlaceholder (node: VNode): boolean { + return node.isComment && node.asyncFactory +} diff --git a/src/platforms/web/runtime/components/transition.js b/src/platforms/web/runtime/components/transition.js index 9ac73b164ec..60b434cdced 100644 --- a/src/platforms/web/runtime/components/transition.js +++ b/src/platforms/web/runtime/components/transition.js @@ -5,7 +5,11 @@ import { warn } from 'core/util/index' import { camelize, extend, isPrimitive } from 'shared/util' -import { mergeVNodeHook, getFirstComponentChild } from 'core/vdom/helpers/index' +import { + mergeVNodeHook, + isAsyncPlaceholder, + getFirstComponentChild +} from 'core/vdom/helpers/index' export const transitionProps = { name: String, @@ -72,10 +76,6 @@ function isSameChild (child: VNode, oldChild: VNode): boolean { return oldChild.key === child.key && oldChild.tag === child.tag } -function isAsyncPlaceholder (node: VNode): boolean { - return node.isComment && node.asyncFactory -} - export default { name: 'transition', props: transitionProps, diff --git a/test/unit/features/component/component-keep-alive.spec.js b/test/unit/features/component/component-keep-alive.spec.js index 1295c17f50e..dd3e3903b83 100644 --- a/test/unit/features/component/component-keep-alive.spec.js +++ b/test/unit/features/component/component-keep-alive.spec.js @@ -862,5 +862,89 @@ describe('Component keep-alive', () => { ) }).then(done) }) + + it('async components with transition-mode out-in', done => { + const barResolve = jasmine.createSpy('bar resolved') + let next + const foo = (resolve) => { + setTimeout(() => { + resolve(one) + Vue.nextTick(next) + }, duration / 2) + } + const bar = (resolve) => { + setTimeout(() => { + resolve(two) + barResolve() + }, duration / 2) + } + components = { + foo, + bar + } + const vm = new Vue({ + template: `
+ + + + + +
`, + data: { + view: 'foo' + }, + components, + methods: { + afterEnter () { + next() + }, + afterLeave () { + next() + } + } + }).$mount(el) + expect(vm.$el.textContent).toBe('') + next = () => { + assertHookCalls(one, [1, 1, 1, 0, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + waitForUpdate(() => { + expect(vm.$el.innerHTML).toBe( + '
one
' + ) + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
one
' + ) + }).thenWaitFor(_next => { next = _next }).then(() => { + // foo afterEnter get called + expect(vm.$el.innerHTML).toBe('
one
') + vm.view = 'bar' + }).thenWaitFor(nextFrame).then(() => { + assertHookCalls(one, [1, 1, 1, 1, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + expect(vm.$el.innerHTML).toBe( + '
one
' + ) + }).thenWaitFor(_next => { next = _next }).then(() => { + // foo afterLeave get called + // and bar has already been resolved before afterLeave get called + expect(barResolve.calls.count()).toBe(1) + expect(vm.$el.innerHTML).toBe('') + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + ) + assertHookCalls(one, [1, 1, 1, 1, 0]) + assertHookCalls(two, [1, 1, 1, 0, 0]) + }).thenWaitFor(nextFrame).then(() => { + expect(vm.$el.innerHTML).toBe( + '
two
' + ) + }).thenWaitFor(_next => { next = _next }).then(() => { + // bar afterEnter get called + expect(vm.$el.innerHTML).toBe('
two
') + }).then(done) + } + }) } })