Browse Source

fix out-in transition for async components (fix #5760)

dev
Evan You 8 years ago
parent
commit
c3cdfcfa68
  1. 15
      src/platforms/web/runtime/components/transition.js
  2. 114
      test/unit/features/transition/transition.spec.js

15
src/platforms/web/runtime/components/transition.js

@ -72,19 +72,23 @@ 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,
abstract: true,
render (h: Function) {
let children: ?Array<VNode> = this.$slots.default
let children: ?Array<VNode> = this.$options._renderChildren
if (!children) {
return
}
// filter out text nodes (possible whitespaces)
children = children.filter((c: VNode) => c.tag)
children = children.filter((c: VNode) => c.tag || isAsyncPlaceholder(c))
/* istanbul ignore if */
if (!children.length) {
return
@ -151,7 +155,12 @@ export default {
child.data.show = true
}
if (oldChild && oldChild.data && !isSameChild(child, oldChild)) {
if (
oldChild &&
oldChild.data &&
!isSameChild(child, oldChild) &&
!isAsyncPlaceholder(oldChild)
) {
// replace old child transition data with fresh one
// important for dynamic transitions!
const oldData: Object = oldChild && (oldChild.data.transition = extend({}, data))

114
test/unit/features/transition/transition.spec.js

@ -877,6 +877,120 @@ if (!isIE9) {
expect(`<transition> can only be used on a single element`).toHaveBeenWarned()
})
it('transition out-in on async component (resolve before leave complete)', done => {
const vm = new Vue({
template: `
<div>
<transition name="test-anim" mode="out-in">
<component-a v-if="ok"></component-a>
<component-b v-else></component-b>
</transition>
</div>
`,
components: {
componentA: resolve => {
setTimeout(() => {
resolve({ template: '<div><h1>component A</h1></div>' })
next1()
}, duration / 2)
},
componentB: resolve => {
setTimeout(() => {
resolve({ template: '<div><h1>component B</h1></div>' })
}, duration / 2)
}
},
data: {
ok: true
}
}).$mount(el)
expect(vm.$el.innerHTML).toBe('<!---->')
function next1 () {
Vue.nextTick(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component A')
expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active')
nextFrame(() => {
expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to')
setTimeout(() => {
expect(vm.$el.children[0].className).toBe('')
vm.ok = false
next2()
}, duration + buffer)
})
})
}
function next2 () {
waitForUpdate(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component A')
expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.children[0].className).toBe('test-anim-leave-active test-anim-leave-to')
}).thenWaitFor(duration + buffer).then(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component B')
expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to')
}).thenWaitFor(duration + buffer).then(() => {
expect(vm.$el.children[0].className).toBe('')
}).then(done)
}
})
it('transition out-in on async component (resolve after leave complete)', done => {
const vm = new Vue({
template: `
<div>
<transition name="test-anim" mode="out-in">
<component-a v-if="ok"></component-a>
<component-b v-else></component-b>
</transition>
</div>
`,
components: {
componentA: { template: '<div><h1>component A</h1></div>' },
componentB: resolve => {
setTimeout(() => {
resolve({ template: '<div><h1>component B</h1></div>' })
Vue.nextTick(next)
}, (duration + buffer) * 1.5)
}
},
data: {
ok: true
}
}).$mount(el)
expect(vm.$el.innerHTML).toBe('<div><h1>component A</h1></div>')
let next
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component A')
expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.children[0].className).toBe('test-anim-leave-active test-anim-leave-to')
}).thenWaitFor(duration + buffer).then(() => {
expect(vm.$el.children.length).toBe(0)
expect(vm.$el.innerHTML).toBe('<!---->')
}).thenWaitFor(_next => { next = _next }).then(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component B')
expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to')
}).thenWaitFor(duration + buffer).then(() => {
expect(vm.$el.children.length).toBe(1)
expect(vm.$el.textContent).toBe('component B')
expect(vm.$el.children[0].className).toBe('')
}).then(done)
})
describe('explicit durations -', () => {
it('single value', done => {
const vm = new Vue({

Loading…
Cancel
Save