Browse Source

fix v-ref for dynamic components with keep-alive (fix #1695)

Evan You 10 năm trước cách đây
mục cha
commit
fbfcfccc9f

+ 8 - 0
src/directives/internal/component.js

@@ -132,6 +132,10 @@ module.exports = {
         self.transition(newComponent, cb)
       })
     } else {
+      // update ref for kept-alive component
+      if (cached) {
+        newComponent._updateRef()
+      }
       this.transition(newComponent, cb)
     }
   },
@@ -241,6 +245,10 @@ module.exports = {
     }
     var child = this.childVM
     if (!child || this.keepAlive) {
+      if (child) {
+        // remove ref
+        child._updateRef(true)
+      }
       return
     }
     // the sole purpose of `deferCleanup` is so that we can

+ 3 - 5
src/instance/init.js

@@ -79,11 +79,6 @@ exports._init = function (options) {
     this.$parent.$children.push(this)
   }
 
-  // set ref
-  if (options._ref) {
-    (this._scope || this._context).$refs[options._ref] = this
-  }
-
   // merge options.
   options = this.$options = mergeOptions(
     this.constructor.options,
@@ -91,6 +86,9 @@ exports._init = function (options) {
     this
   )
 
+  // set ref
+  this._updateRef()
+
   // initialize data as empty object.
   // it will be filled up in _initScope().
   this._data = {}

+ 22 - 8
src/instance/lifecycle.js

@@ -2,6 +2,26 @@ var _ = require('../util')
 var Directive = require('../directive')
 var compiler = require('../compiler')
 
+/**
+ * Update v-ref for component.
+ *
+ * @param {Boolean} remove
+ */
+
+exports._updateRef = function (remove) {
+  var ref = this.$options._ref
+  if (ref) {
+    var refs = (this._scope || this._context).$refs
+    if (remove) {
+      if (refs[ref] === this) {
+        refs[ref] = null
+      }
+    } else {
+      refs[ref] = this
+    }
+  }
+}
+
 /**
  * Transclude, compile and link element.
  *
@@ -136,14 +156,8 @@ exports._destroy = function (remove, deferCleanup) {
   var parent = this.$parent
   if (parent && !parent._isBeingDestroyed) {
     parent.$children.$remove(this)
-    // unregister ref
-    var ref = this.$options._ref
-    if (ref) {
-      var scope = this._scope || this._context
-      if (scope.$refs[ref] === this) {
-        scope.$refs[ref] = null
-      }
-    }
+    // unregister ref (remove: true)
+    this._updateRef(true)
   }
   // destroy all children.
   i = this.$children.length

+ 19 - 0
test/unit/specs/directives/public/ref_spec.js

@@ -53,6 +53,25 @@ if (_.inBrowser) {
       })
     })
 
+    it('with dynamic component + keep-alive', function (done) {
+      var vm = new Vue({
+        el: el,
+        components: components,
+        data: { test: 'test' },
+        template: '<component :is="test" v-ref:test keep-alive></component>'
+      })
+      expect(vm.$refs.test.$options.id).toBe('test')
+      vm.test = 'test2'
+      _.nextTick(function () {
+        expect(vm.$refs.test.$options.id).toBe('test2')
+        vm.test = ''
+        _.nextTick(function () {
+          expect(vm.$refs.test).toBe(null)
+          done()
+        })
+      })
+    })
+
     it('should be reactive when bound by dynamic component and hoisted', function (done) {
       var vm = new Vue({
         el: el,

+ 2 - 0
test/unit/specs/instance/init_spec.js

@@ -6,6 +6,7 @@ describe('Instance Init', function () {
     constructor: {
       options: { a: 1, b: 2 }
     },
+    _updateRef: jasmine.createSpy(),
     _initEvents: jasmine.createSpy(),
     _callHook: jasmine.createSpy(),
     _initState: jasmine.createSpy(),
@@ -38,6 +39,7 @@ describe('Instance Init', function () {
   it('should call other init methods', function () {
     expect(stub._initEvents).toHaveBeenCalled()
     expect(stub._initState).toHaveBeenCalled()
+    expect(stub._updateRef).toHaveBeenCalled()
   })
 
   it('should call created hook', function () {