Evan You před 10 roky
rodič
revize
f844961580

+ 7 - 0
src/deprecations.js

@@ -156,6 +156,13 @@ if (process.env.NODE_ENV !== 'production') {
         '<component is="{{view}}"> syntax will be depreacted in 1.0.0. Use ' +
         '<component bind-is="view"> instead.'
       )
+    },
+
+    REF_IN_CHILD: function () {
+      warn(
+        'v-ref or ref can no longer be used on a component root in its own ' +
+        'template in 1.0.0. Use it in the parent template instead.'
+      )
     }
 
   }

+ 4 - 0
src/directive.js

@@ -191,6 +191,10 @@ Directive.prototype.param = function (name) {
     if (param != null) {
       this.el.removeAttribute('bind-' + name)
       param = (this._scope || this.vm).$eval(param)
+      process.env.NODE_ENV !== 'production' && _.log(
+        'You are using bind- syntax on "' + name + '", which ' +
+        'is a directive param. It will be evaluated only once.'
+      )
     }
   }
   return param

+ 20 - 7
src/directives/component.js

@@ -34,7 +34,14 @@ module.exports = {
       }
 
       // check ref
-      this.refID = this.param(config.prefix + 'ref')
+      // TODO: only check ref in 1.0.0
+      var ref = this.param(config.prefix + 'ref')
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production' && ref) {
+        _.deprecation.V_REF()
+      }
+      this.ref = ref || this.param('ref')
+
       if (this.keepAlive) {
         this.cache = {}
       }
@@ -336,9 +343,14 @@ module.exports = {
   setCurrent: function (child) {
     this.unsetCurrent()
     this.childVM = child
-    var refID = child._refID || this.refID
-    if (refID) {
-      this.vm.$[refID] = child
+    var ref = child._refId || this.ref
+    if (ref) {
+      var hash = (this._scope || this.vm).$
+      if (!hash.hasOwnProperty(ref)) {
+        _.defineReactive(hash, ref, child)
+      } else {
+        hash[ref] = child
+      }
     }
   },
 
@@ -349,9 +361,10 @@ module.exports = {
   unsetCurrent: function () {
     var child = this.childVM
     this.childVM = null
-    var refID = (child && child._refID) || this.refID
-    if (refID) {
-      this.vm.$[refID] = null
+    var ref = (child && child._refId) || this.ref
+    var hash = (this._scope || this.vm).$
+    if (ref && hash.hasOwnProperty(ref)) {
+      hash[ref] = null
     }
   },
 

+ 15 - 5
src/directives/for.js

@@ -55,8 +55,8 @@ module.exports = {
     // check v-ref
     var ref = this.param(config.prefix + 'ref')
     /* istanbul ignore if */
-    if (process.env.NODE_ENV !== 'production') {
-      if (this.refID) _.deprecation.V_REF()
+    if (process.env.NODE_ENV !== 'production' && ref) {
+      _.deprecation.V_REF()
     }
     this.ref = ref || this.param('ref')
 
@@ -206,6 +206,8 @@ module.exports = {
     // create iteration scope
     var parentScope = this._scope || this.vm
     var scope = Object.create(parentScope)
+    // ref holder for the scope
+    scope.$ = {}
     // make sure point $parent to parent scope
     scope.$parent = parentScope
     // for two-way binding on alias
@@ -230,14 +232,22 @@ module.exports = {
    */
 
   updateRef: function () {
+    var ref = this.ref
+    var hash = (this._scope || this.vm).$
+    var refs
     if (!this.converted) {
-      this.vm.$[this.ref] = this.frags.map(findVmFromFrag)
+      refs = this.frags.map(findVmFromFrag)
     } else {
-      var refs = this.vm.$[this.ref] = {}
+      refs = {}
       this.frags.forEach(function (frag) {
         refs[frag.scope.$key] = findVmFromFrag(frag)
       })
     }
+    if (!hash.hasOwnProperty(ref)) {
+      _.defineReactive(hash, ref, refs)
+    } else {
+      hash[ref] = refs
+    }
   },
 
   /**
@@ -480,7 +490,7 @@ module.exports = {
 
   unbind: function () {
     if (this.ref) {
-      this.vm.$[this.ref] = null
+      (this._scope || this.vm).$[this.ref] = null
     }
     if (this.frags) {
       var i = this.frags.length

+ 5 - 1
src/directives/ref.js

@@ -17,6 +17,10 @@ module.exports = {
     // `v-component` already. So we just record our own ref
     // here - it will overwrite parent ref in `v-component`,
     // if any.
-    vm._refID = this.expression
+    vm._refId = this.expression
+
+    if (process.env.NODE_ENV !== 'production') {
+      _.deprecation.REF_IN_CHILD()
+    }
   }
 }

+ 7 - 7
src/directives/repeat.js

@@ -69,14 +69,14 @@ module.exports = {
     this.leaveStagger = +this.param('leave-stagger') || stagger
 
     // check for v-ref/v-el
-    this.refID = this.param(config.prefix + 'ref')
+    this.refId = this.param(config.prefix + 'ref')
     this.elID = this.param(config.prefix + 'el')
 
     if (process.env.NODE_ENV !== 'production') {
-      if (this.refID) _.deprecation.V_REF()
+      if (this.refId) _.deprecation.V_REF()
       if (this.elID) _.deprecation.V_EL()
     }
-    this.refID = this.refID || this.param('ref')
+    this.refId = this.refId || this.param('ref')
 
     // check other directives that need to be handled
     // at v-repeat level
@@ -228,8 +228,8 @@ module.exports = {
   realUpdate: function (data) {
     this.vms = this.diff(data, this.vms)
     // update v-ref
-    if (this.refID) {
-      this.vm.$[this.refID] = this.converted
+    if (this.refId) {
+      this.vm.$[this.refId] = this.converted
         ? toRefObject(this.vms)
         : this.vms
     }
@@ -440,8 +440,8 @@ module.exports = {
 
   unbind: function () {
     this.componentState = ABORTED
-    if (this.refID) {
-      this.vm.$[this.refID] = null
+    if (this.refId) {
+      this.vm.$[this.refId] = null
     }
     if (this.vms) {
       var i = this.vms.length

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

@@ -2,7 +2,7 @@ var _ = require('../../../../src/util')
 var Vue = require('../../../../src/vue')
 
 if (_.inBrowser) {
-  describe('v-component', function () {
+  describe('Component', function () {
 
     var el
     beforeEach(function () {
@@ -531,6 +531,5 @@ if (_.inBrowser) {
         })
       }).not.toThrow()
     })
-
   })
 }

+ 6 - 6
test/unit/specs/directives/for/for_spec.js

@@ -664,9 +664,9 @@ if (_.inBrowser) {
       var vm = new Vue({
         el: el,
         template:
-          '<div v-for="item in list" track-by="id" ref="outer">' +
+          '<div v-for="item in list" track-by="id">' +
             '{{item.msg}}' +
-            '<div v-for="subItem in item.list" track-by="id" bind-ref="\'inner\' + $index">' +
+            '<div v-for="subItem in item.list" track-by="id">' +
               '{{subItem.msg}}' +
             '</div>' +
           '</div>',
@@ -682,8 +682,8 @@ if (_.inBrowser) {
       })
       assertMarkup()
 
-      var oldFrags = vm.$.outer
-      var oldInnerFrags = vm.$.inner0
+      var oldNodes = el.children
+      var oldInnerNodes = el.children[0].children
 
       vm.list = [
         { id: 1, msg: 'wa', list: [
@@ -698,9 +698,9 @@ if (_.inBrowser) {
         // should reuse old frags!
         var i = 2
         while (i--) {
-          expect(vm.$.outer[i]).toBe(oldFrags[i])
+          expect(el.children[i]).toBe(oldNodes[i])
         }
-        expect(vm.$.inner0[0]).toBe(oldInnerFrags[0])
+        expect(el.children[0].children[0]).toBe(oldInnerNodes[0])
         done()
       })
 

+ 44 - 2
test/unit/specs/directives/ref_spec.js

@@ -23,10 +23,15 @@ if (_.inBrowser) {
       var vm = new Vue({
         el: el,
         components: components,
-        template: '<test v-ref="test"></test>'
+        data: {
+          ref: 'test2'
+        },
+        template: '<test ref="test"></test><test2 bind-ref="ref"></test2>'
       })
       expect(vm.$.test).toBeTruthy()
       expect(vm.$.test.$options.id).toBe('test')
+      expect(vm.$.test2).toBeTruthy()
+      expect(vm.$.test2.$options.id).toBe('test2')
     })
 
     it('with dynamic v-component', function (done) {
@@ -34,7 +39,7 @@ if (_.inBrowser) {
         el: el,
         components: components,
         data: { test: 'test' },
-        template: '<component is="{{test}}" v-ref="test"></component>'
+        template: '<component bind-is="test" ref="test"></component>'
       })
       expect(vm.$.test.$options.id).toBe('test')
       vm.test = 'test2'
@@ -48,6 +53,43 @@ if (_.inBrowser) {
       })
     })
 
+    it('should be reactive when bound by dynamic component', function (done) {
+      var vm = new Vue({
+        el: el,
+        data: { view: 'one' },
+        template: '<component bind-is="view" ref="test"></component>{{$.test.value}}',
+        components: {
+          one: {
+            id: 'one',
+            replace: true,
+            data: function () {
+              return { value: 1 }
+            }
+          },
+          two: {
+            id: 'two',
+            replace: true,
+            data: function () {
+              return { value: 2 }
+            }
+          }
+        }
+      })
+      expect(vm.$.test.$options.id).toBe('one')
+      expect(el.textContent).toBe('1')
+      vm.view = 'two'
+      _.nextTick(function () {
+        expect(vm.$.test.$options.id).toBe('two')
+        expect(el.textContent).toBe('2')
+        vm.view = ''
+        _.nextTick(function () {
+          expect(vm.$.test).toBeNull()
+          expect(el.textContent).toBe('')
+          done()
+        })
+      })
+    })
+
     it('should also work in child template', function (done) {
       var vm = new Vue({
         el: el,

+ 1 - 0
test/unit/specs/instance/state_spec.js

@@ -165,6 +165,7 @@ describe('Instance state initialization', function () {
           return this.a + this.b
         },
         d: {
+          cache: false, // for deprecation coverage. TODO: remove in 1.0.0
           get: function () {
             return this.a + this.b
           },