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

fix(transition): consider async placeholder as valid child to return (#6369)

fix #6256
JK пре 8 година
родитељ
комит
a43d66743b

+ 2 - 1
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>): ?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
       }
     }

+ 1 - 0
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'

+ 5 - 0
src/core/vdom/helpers/is-async-placeholder.js

@@ -0,0 +1,5 @@
+/* @flow */
+
+export function isAsyncPlaceholder (node: VNode): boolean {
+  return node.isComment && node.asyncFactory
+}

+ 5 - 5
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,

+ 84 - 0
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: `<div>
+          <transition name="test" mode="out-in" @after-enter="afterEnter" @after-leave="afterLeave">
+            <keep-alive>
+              <component :is="view" class="test"></component>
+            </keep-alive>
+          </transition>
+        </div>`,
+        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(
+            '<div class="test test-enter test-enter-active">one</div>'
+          )
+        }).thenWaitFor(nextFrame).then(() => {
+          expect(vm.$el.innerHTML).toBe(
+            '<div class="test test-enter-active test-enter-to">one</div>'
+          )
+        }).thenWaitFor(_next => { next = _next }).then(() => {
+          // foo afterEnter get called
+          expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
+          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(
+            '<div class="test test-leave-active test-leave-to">one</div><!---->'
+          )
+        }).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(
+            '<div class="test test-enter test-enter-active">two</div>'
+          )
+          assertHookCalls(one, [1, 1, 1, 1, 0])
+          assertHookCalls(two, [1, 1, 1, 0, 0])
+        }).thenWaitFor(nextFrame).then(() => {
+          expect(vm.$el.innerHTML).toBe(
+            '<div class="test test-enter-active test-enter-to">two</div>'
+          )
+        }).thenWaitFor(_next => { next = _next }).then(() => {
+          // bar afterEnter get called
+          expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
+        }).then(done)
+      }
+    })
   }
 })