Evan You 10 лет назад
Родитель
Сommit
e433032f8f

+ 6 - 2
src/core/components/keep-alive.js

@@ -1,3 +1,5 @@
+import { callHook } from 'core/instance/lifecycle'
+
 export default {
 export default {
   name: 'keep-alive',
   name: 'keep-alive',
   _abstract: true,
   _abstract: true,
@@ -20,9 +22,11 @@ export default {
     realChild.data.keepAlive = true
     realChild.data.keepAlive = true
     return rawChild
     return rawChild
   },
   },
-  beforeDestroy () {
+  destroyed () {
     for (const key in this.cache) {
     for (const key in this.cache) {
-      this.cache[key].child.$destroy()
+      const vnode = this.cache[key]
+      callHook(vnode.child, 'deactivated')
+      vnode.child.$destroy()
     }
     }
   }
   }
 }
 }

+ 6 - 4
src/core/vdom/create-component.js

@@ -128,10 +128,12 @@ function insert (vnode: MountedComponentVNode) {
 }
 }
 
 
 function destroy (vnode: MountedComponentVNode) {
 function destroy (vnode: MountedComponentVNode) {
-  if (!vnode.data.keepAlive) {
-    vnode.child.$destroy()
-  } else {
-    callHook(vnode.child, 'deactivated')
+  if (!vnode.child._isDestroyed) {
+    if (!vnode.data.keepAlive) {
+      vnode.child.$destroy()
+    } else {
+      callHook(vnode.child, 'deactivated')
+    }
   }
   }
 }
 }
 
 

+ 7 - 7
src/core/vdom/patch.js

@@ -126,13 +126,13 @@ export function createPatchFunction (backend) {
     if (isDef(data)) {
     if (isDef(data)) {
       if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode)
       if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode)
       for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode)
       for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode)
-      if (isDef(i = vnode.children)) {
-        for (j = 0; j < vnode.children.length; ++j) {
-          invokeDestroyHook(vnode.children[j])
-        }
-      }
-      if (isDef(i = vnode.child)) {
-        invokeDestroyHook(i._vnode)
+    }
+    if (isDef(i = vnode.child)) {
+      invokeDestroyHook(i._vnode)
+    }
+    if (isDef(i = vnode.children)) {
+      for (j = 0; j < vnode.children.length; ++j) {
+        invokeDestroyHook(vnode.children[j])
       }
       }
     }
     }
   }
   }

+ 46 - 37
test/unit/features/component/component-keep-alive.spec.js

@@ -12,14 +12,16 @@ describe('Component keep-alive', () => {
       created: jasmine.createSpy('one created'),
       created: jasmine.createSpy('one created'),
       mounted: jasmine.createSpy('one mounted'),
       mounted: jasmine.createSpy('one mounted'),
       activated: jasmine.createSpy('one activated'),
       activated: jasmine.createSpy('one activated'),
-      deactivated: jasmine.createSpy('one deactivated')
+      deactivated: jasmine.createSpy('one deactivated'),
+      destroyed: jasmine.createSpy('one destroyed')
     }
     }
     two = {
     two = {
       template: '<div>two</div>',
       template: '<div>two</div>',
       created: jasmine.createSpy('two created'),
       created: jasmine.createSpy('two created'),
       mounted: jasmine.createSpy('two mounted'),
       mounted: jasmine.createSpy('two mounted'),
       activated: jasmine.createSpy('two activated'),
       activated: jasmine.createSpy('two activated'),
-      deactivated: jasmine.createSpy('two deactivated')
+      deactivated: jasmine.createSpy('two deactivated'),
+      destroyed: jasmine.createSpy('two destroyed')
     }
     }
     components = {
     components = {
       one,
       one,
@@ -34,36 +36,43 @@ describe('Component keep-alive', () => {
       component.created.calls.count(),
       component.created.calls.count(),
       component.mounted.calls.count(),
       component.mounted.calls.count(),
       component.activated.calls.count(),
       component.activated.calls.count(),
-      component.deactivated.calls.count()
+      component.deactivated.calls.count(),
+      component.destroyed.calls.count()
     ]).toEqual(callCounts)
     ]).toEqual(callCounts)
   }
   }
 
 
   it('should work', done => {
   it('should work', done => {
     const vm = new Vue({
     const vm = new Vue({
-      template: '<div><component :is="view" keep-alive></component></div>',
+      template: '<div v-if="ok"><component :is="view" keep-alive></component></div>',
       data: {
       data: {
-        view: 'one'
+        view: 'one',
+        ok: true
       },
       },
       components
       components
     }).$mount()
     }).$mount()
     expect(vm.$el.textContent).toBe('one')
     expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0])
-    assertHookCalls(two, [0, 0, 0, 0])
+    assertHookCalls(one, [1, 1, 1, 0, 0])
+    assertHookCalls(two, [0, 0, 0, 0, 0])
     vm.view = 'two'
     vm.view = 'two'
     waitForUpdate(() => {
     waitForUpdate(() => {
       expect(vm.$el.textContent).toBe('two')
       expect(vm.$el.textContent).toBe('two')
-      assertHookCalls(one, [1, 1, 1, 1])
-      assertHookCalls(two, [1, 1, 1, 0])
+      assertHookCalls(one, [1, 1, 1, 1, 0])
+      assertHookCalls(two, [1, 1, 1, 0, 0])
       vm.view = 'one'
       vm.view = 'one'
     }).then(() => {
     }).then(() => {
       expect(vm.$el.textContent).toBe('one')
       expect(vm.$el.textContent).toBe('one')
-      assertHookCalls(one, [1, 1, 2, 1])
-      assertHookCalls(two, [1, 1, 1, 1])
+      assertHookCalls(one, [1, 1, 2, 1, 0])
+      assertHookCalls(two, [1, 1, 1, 1, 0])
       vm.view = 'two'
       vm.view = 'two'
     }).then(() => {
     }).then(() => {
       expect(vm.$el.textContent).toBe('two')
       expect(vm.$el.textContent).toBe('two')
-      assertHookCalls(one, [1, 1, 2, 2])
-      assertHookCalls(two, [1, 1, 2, 1])
+      assertHookCalls(one, [1, 1, 2, 2, 0])
+      assertHookCalls(two, [1, 1, 2, 1, 0])
+      vm.ok = false // teardown
+    }).then(() => {
+      expect(vm.$el.textContent).toBe('')
+      assertHookCalls(one, [1, 1, 2, 3, 1])
+      assertHookCalls(two, [1, 1, 2, 2, 1])
     }).then(done)
     }).then(done)
   })
   })
 
 
@@ -93,15 +102,15 @@ describe('Component keep-alive', () => {
         }
         }
       }).$mount(el)
       }).$mount(el)
       expect(vm.$el.textContent).toBe('one')
       expect(vm.$el.textContent).toBe('one')
-      assertHookCalls(one, [1, 1, 1, 0])
-      assertHookCalls(two, [0, 0, 0, 0])
+      assertHookCalls(one, [1, 1, 1, 0, 0])
+      assertHookCalls(two, [0, 0, 0, 0, 0])
       vm.view = 'two'
       vm.view = 'two'
       waitForUpdate(() => {
       waitForUpdate(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-leave">one</div>'
           '<div class="test test-leave">one</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [0, 0, 0, 0])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [0, 0, 0, 0, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-leave-active">one</div>'
           '<div class="test test-leave-active">one</div>'
@@ -112,8 +121,8 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-enter">two</div>'
           '<div class="test test-enter">two</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 0])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 0, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-enter-active">two</div>'
           '<div class="test test-enter-active">two</div>'
@@ -122,16 +131,16 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">two</div>'
           '<div class="test">two</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 0])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 0, 0])
       }).then(() => {
       }).then(() => {
         vm.view = 'one'
         vm.view = 'one'
       }).then(() => {
       }).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-leave">two</div>'
           '<div class="test test-leave">two</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 1])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 1, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-leave-active">two</div>'
           '<div class="test test-leave-active">two</div>'
@@ -142,8 +151,8 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-enter">one</div>'
           '<div class="test test-enter">one</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 2, 1])
-        assertHookCalls(two, [1, 1, 1, 1])
+        assertHookCalls(one, [1, 1, 2, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 1, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test test-enter-active">one</div>'
           '<div class="test test-enter-active">one</div>'
@@ -152,8 +161,8 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">one</div>'
           '<div class="test">one</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 2, 1])
-        assertHookCalls(two, [1, 1, 1, 1])
+        assertHookCalls(one, [1, 1, 2, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 1, 0])
       }).then(done)
       }).then(done)
     })
     })
 
 
@@ -182,16 +191,16 @@ describe('Component keep-alive', () => {
         }
         }
       }).$mount(el)
       }).$mount(el)
       expect(vm.$el.textContent).toBe('one')
       expect(vm.$el.textContent).toBe('one')
-      assertHookCalls(one, [1, 1, 1, 0])
-      assertHookCalls(two, [0, 0, 0, 0])
+      assertHookCalls(one, [1, 1, 1, 0, 0])
+      assertHookCalls(two, [0, 0, 0, 0, 0])
       vm.view = 'two'
       vm.view = 'two'
       waitForUpdate(() => {
       waitForUpdate(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">one</div>' +
           '<div class="test">one</div>' +
           '<div class="test test-enter">two</div>'
           '<div class="test test-enter">two</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 0])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 0, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">one</div>' +
           '<div class="test">one</div>' +
@@ -216,8 +225,8 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">two</div>'
           '<div class="test">two</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 0])
+        assertHookCalls(one, [1, 1, 1, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 0, 0])
       }).then(() => {
       }).then(() => {
         vm.view = 'one'
         vm.view = 'one'
       }).then(() => {
       }).then(() => {
@@ -225,8 +234,8 @@ describe('Component keep-alive', () => {
           '<div class="test">two</div>' +
           '<div class="test">two</div>' +
           '<div class="test test-enter">one</div>'
           '<div class="test test-enter">one</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 2, 1])
-        assertHookCalls(two, [1, 1, 1, 1])
+        assertHookCalls(one, [1, 1, 2, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 1, 0])
       }).thenWaitFor(nextFrame).then(() => {
       }).thenWaitFor(nextFrame).then(() => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">two</div>' +
           '<div class="test">two</div>' +
@@ -251,8 +260,8 @@ describe('Component keep-alive', () => {
         expect(vm.$el.innerHTML).toBe(
         expect(vm.$el.innerHTML).toBe(
           '<div class="test">one</div>'
           '<div class="test">one</div>'
         )
         )
-        assertHookCalls(one, [1, 1, 2, 1])
-        assertHookCalls(two, [1, 1, 1, 1])
+        assertHookCalls(one, [1, 1, 2, 1, 0])
+        assertHookCalls(two, [1, 1, 1, 1, 0])
       }).then(done)
       }).then(done)
     })
     })
   }
   }