Просмотр исходного кода

deactivate current component during v-component transitions

Evan You 11 лет назад
Родитель
Сommit
3ae384c10d
3 измененных файлов с 72 добавлено и 60 удалено
  1. 20 22
      src/api/lifecycle.js
  2. 50 37
      src/directives/component.js
  3. 2 1
      test/unit/specs/directives/component_spec.js

+ 20 - 22
src/api/lifecycle.js

@@ -55,11 +55,12 @@ function ready () {
  * directives, turn off all the event listeners, etc.
  *
  * @param {Boolean} remove - whether to remove the DOM node.
- * @param {Function} cb
+ * @param {Boolean} deferCleanup - if true, defer cleanup to
+ *                                 be called later
  * @public
  */
 
-exports.$destroy = function (remove, cb) {
+exports.$destroy = function (remove, deferCleanup) {
   if (this._isBeingDestroyed) {
     return
   }
@@ -98,11 +99,10 @@ exports.$destroy = function (remove, cb) {
   var self = this
   if (remove && this.$el) {
     this.$remove(function () {
-      cleanup(self)
-      if (cb) cb()
+      self._cleanup()
     })
-  } else {
-    cleanup(self)
+  } else if (!deferCleanup) {
+    this._cleanup()
   }
 }
 
@@ -110,27 +110,25 @@ exports.$destroy = function (remove, cb) {
  * Clean up to ensure garbage collection.
  * This is called after the leave transition if there
  * is any.
- *
- * @param {Vue} vm
  */
 
-function cleanup (vm) {
+exports._cleanup = function () {
   // remove reference from data ob
-  vm._data.__ob__.removeVm(vm)
-  vm._data =
-  vm._watchers =
-  vm._userWatchers =
-  vm._watcherList =
-  vm.$el =
-  vm.$parent =
-  vm.$root =
-  vm._children =
-  vm._directives = null
+  this._data.__ob__.removeVm(this)
+  this._data =
+  this._watchers =
+  this._userWatchers =
+  this._watcherList =
+  this.$el =
+  this.$parent =
+  this.$root =
+  this._children =
+  this._directives = null
   // call the last hook...
-  vm._isDestroyed = true
-  vm._callHook('destroyed')
+  this._isDestroyed = true
+  this._callHook('destroyed')
   // turn off all instance listeners.
-  vm.$off()
+  this.$off()
 }
 
 /**

+ 50 - 37
src/directives/component.js

@@ -99,28 +99,41 @@ module.exports = {
   },
 
   /**
-   * Teardown the active vm.
-   * If keep alive, simply remove it; otherwise destroy it.
-   *
-   * @param {Boolean} remove
-   * @param {Function} cb
+   * Teardown the current child, but defers cleanup so
+   * that we can separate the destroy and removal steps.
    */
 
-  unbuild: function (remove, cb) {
+  unbuild: function () {
     var child = this.childVM
-    if (!child) {
-      if (cb) cb()
+    if (!child || this.keepAlive) {
       return
     }
-    if (this.keepAlive) {
-      if (remove) {
-        child.$remove(cb)
-      }
-    } else {
-      if (child._parentUnlinkFn) {
-        child._parentUnlinkFn()
-      }
-      child.$destroy(remove, cb)
+    if (child._parentUnlinkFn) {
+      child._parentUnlinkFn()
+    }
+    // the sole purpose of `deferCleanup` is so that we can
+    // "deactivate" the vm right now and perform DOM removal
+    // later.
+    child.$destroy(false, true)
+  },
+
+  /**
+   * Remove current destroyed child and manually do
+   * the cleanup after removal.
+   *
+   * @param {Function} cb
+   */
+
+  removeCurrent: function (cb) {
+    var child = this.childVM
+    var keepAlive = this.keepAlive
+    if (child) {
+      child.$remove(function () {
+        if (!keepAlive) child._cleanup()
+        if (cb) cb()
+      })
+    } else if (cb) {
+      cb()
     }
   },
 
@@ -131,18 +144,21 @@ module.exports = {
 
   update: function (value) {
     if (!value) {
-      this.unbuild(true)
+      // just destroy and remove current
+      this.unbuild()
+      this.removeCurrent()
       this.childVM = null
     } else {
       this.resolveCtor(value)
-      var child = this.build()
+      this.unbuild()
+      var newComponent = this.build()
       var self = this
       if (this.readyEvent) {
-        child.$once(this.readyEvent, function () {
-          self.swapTo(child)
+        newComponent.$once(this.readyEvent, function () {
+          self.swapTo(newComponent)
         })
       } else {
-        this.swapTo(child)
+        this.swapTo(newComponent)
       }
     }
   },
@@ -151,41 +167,38 @@ module.exports = {
    * Actually swap the components, depending on the
    * transition mode. Defaults to simultaneous.
    *
-   * @param {Vue} child - target to swap to
+   * @param {Vue} target
    */
 
-  swapTo: function (child) {
+  swapTo: function (target) {
     var self = this
     switch (self.transMode) {
       case 'in-out':
-        child.$before(self.ref, function () {
-          self.unbuild(true)
-          self.childVM = child
+        target.$before(self.ref, function () {
+          self.removeCurrent()
+          self.childVM = target
         })
         break
       case 'out-in':
-        self.unbuild(true, function () {
-          child.$before(self.ref)
-          self.childVM = child
+        self.removeCurrent(function () {
+          target.$before(self.ref)
+          self.childVM = target
         })
         break
       default:
-        self.unbuild(true)
-        child.$before(self.ref)
-        self.childVM = child
+        self.removeCurrent()
+        target.$before(self.ref)
+        self.childVM = target
     }
   },
 
   /**
    * Unbind.
-   * Make sure keepAlive is set to false so that the
-   * instance is always destroyed.
    */
 
   unbind: function () {
-    this.keepAlive = false
     this.unbuild()
-    // destroy all cached instances
+    // destroy all keep-alive cached instances
     if (this.cache) {
       for (var key in this.cache) {
         var child = this.cache[key]

+ 2 - 1
test/unit/specs/directives/component_spec.js

@@ -219,7 +219,8 @@ if (_.inBrowser) {
       vm.view = 'b'
       _.nextTick(function () {
         expect(el.textContent).toBe('AAA')
-        vm._children[1].$emit('ok')
+        // old vm is already removed, this is the new vm
+        vm._children[0].$emit('ok')
         expect(el.textContent).toBe('BBB')
         done()
       })