Skip to content

Commit db0bf76

Browse files
committed
fix keep-alive component inner transition (fix vuejs#4339)
1 parent de7764a commit db0bf76

File tree

3 files changed

+69
-9
lines changed

3 files changed

+69
-9
lines changed

src/core/vdom/patch.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { registerRef } from './modules/ref'
2020

2121
export const emptyNode = new VNode('', {}, [])
2222

23-
const hooks = ['create', 'update', 'remove', 'destroy']
23+
const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
2424

2525
function isUndef (s) {
2626
return s == null
@@ -171,15 +171,35 @@ export function createPatchFunction (backend) {
171171
if (isDef(vnode.child)) {
172172
initComponent(vnode, insertedVnodeQueue)
173173
if (isReactivated) {
174-
// unlike a newly created component,
175-
// a reactivated keep-alive component doesn't insert itself
176-
insert(parentElm, vnode.elm, refElm)
174+
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
177175
}
178176
return true
179177
}
180178
}
181179
}
182180

181+
function reactivateComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
182+
let i
183+
// hack for #4339: a reactivated component with inner transition
184+
// does not trigger because the inner node's created hooks are not called
185+
// again. It's not ideal to involve module-specific logic in here but
186+
// there doesn't seem to be a better way to do it.
187+
let innerNode = vnode
188+
while (innerNode.child) {
189+
innerNode = innerNode.child._vnode
190+
if (isDef(i = innerNode.data) && isDef(i = i.transition)) {
191+
for (i = 0; i < cbs.activate.length; ++i) {
192+
cbs.activate[i](emptyNode, innerNode)
193+
}
194+
insertedVnodeQueue.push(innerNode)
195+
break
196+
}
197+
}
198+
// unlike a newly created component,
199+
// a reactivated keep-alive component doesn't insert itself
200+
insert(parentElm, vnode.elm, refElm)
201+
}
202+
183203
function insert (parent, elm, ref) {
184204
if (parent) {
185205
nodeOps.insertBefore(parent, elm, ref)

src/platforms/web/runtime/modules/transition.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,15 @@ function once (fn: Function): Function {
253253
}
254254
}
255255

256+
function _enter (_: any, vnode: VNodeWithData) {
257+
if (!vnode.data.show) {
258+
enter(vnode)
259+
}
260+
}
261+
256262
export default inBrowser ? {
257-
create (_: any, vnode: VNodeWithData) {
258-
if (!vnode.data.show) {
259-
enter(vnode)
260-
}
261-
},
263+
create: _enter,
264+
activate: _enter,
262265
remove (vnode: VNode, rm: Function) {
263266
/* istanbul ignore else */
264267
if (!vnode.data.show) {

test/unit/features/component/component-keep-alive.spec.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,5 +504,42 @@ describe('Component keep-alive', () => {
504504
)
505505
}).then(done).then(done)
506506
})
507+
508+
// #4339
509+
it('component with inner transition', done => {
510+
const vm = new Vue({
511+
template: `
512+
<div>
513+
<keep-alive>
514+
<component ref="test" :is="view"></component>
515+
</keep-alive>
516+
</div>
517+
`,
518+
data: { view: 'foo' },
519+
components: {
520+
foo: { template: '<transition><div class="test">foo</div></transition>' },
521+
bar: { template: '<transition name="test"><div class="test">bar</div></transition>' }
522+
}
523+
}).$mount(el)
524+
525+
// should not apply transition on initial render by default
526+
expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
527+
vm.view = 'bar'
528+
waitForUpdate(() => {
529+
expect(vm.$el.innerHTML).toBe(
530+
'<div class="test v-leave v-leave-active">foo</div>' +
531+
'<div class="test test-enter test-enter-active">bar</div>'
532+
)
533+
}).thenWaitFor(nextFrame).then(() => {
534+
expect(vm.$el.innerHTML).toBe(
535+
'<div class="test v-leave-active">foo</div>' +
536+
'<div class="test test-enter-active">bar</div>'
537+
)
538+
}).thenWaitFor(duration + buffer).then(() => {
539+
expect(vm.$el.innerHTML).toBe(
540+
'<div class="test">bar</div>'
541+
)
542+
}).then(done)
543+
})
507544
}
508545
})

0 commit comments

Comments
 (0)