Browse Source

hoist component binding

Evan You 10 years ago
parent
commit
8153977714

+ 8 - 1
src/compiler/compile.js

@@ -86,7 +86,14 @@ exports.compile = function (el, options, partial) {
 function linkAndCapture (linker, vm) {
   var originalDirCount = vm._directives.length
   linker()
-  return vm._directives.slice(originalDirCount)
+  var dirs = vm._directives.slice(originalDirCount)
+  for (var i = 0, l = dirs.length; i < l; i++) {
+    if (dirs[i].name === 'component') dirs[i]._bind()
+  }
+  for (var i = 0, l = dirs.length; i < l; i++) {
+    if (dirs[i].name !== 'component') dirs[i]._bind()
+  }
+  return dirs
 }
 
 /**

+ 3 - 3
src/directive.js

@@ -36,6 +36,7 @@ function Directive (name, el, vm, descriptor, def, host, scope, frag, arg) {
   this.arg = arg || descriptor.arg
   this.filters = descriptor.filters
   // private
+  this._def = def
   this._descriptor = descriptor
   this._locked = false
   this._bound = false
@@ -44,8 +45,6 @@ function Directive (name, el, vm, descriptor, def, host, scope, frag, arg) {
   this._host = host
   this._scope = scope
   this._frag = frag
-  // init
-  this._bind(def)
 }
 
 /**
@@ -56,7 +55,8 @@ function Directive (name, el, vm, descriptor, def, host, scope, frag, arg) {
  * @param {Object} def
  */
 
-Directive.prototype._bind = function (def) {
+Directive.prototype._bind = function () {
+  var def = this._def
   var name = this.name
   if (
     (name !== 'cloak' || this.vm._isCompiled) &&

+ 6 - 9
src/directives/component.js

@@ -41,6 +41,9 @@ module.exports = {
         _.deprecation.V_REF()
       }
       this.ref = ref || this.param('ref')
+      if (this.ref) {
+        _.defineReactive((this._scope || this.vm).$, this.ref, null)
+      }
 
       if (this.keepAlive) {
         this.cache = {}
@@ -345,12 +348,7 @@ module.exports = {
     this.childVM = 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
-      }
+      (this._scope || this.vm).$[ref] = child
     }
   },
 
@@ -362,9 +360,8 @@ module.exports = {
     var child = this.childVM
     this.childVM = null
     var ref = (child && child._refId) || this.ref
-    var hash = (this._scope || this.vm).$
-    if (ref && hash.hasOwnProperty(ref)) {
-      hash[ref] = null
+    if (ref) {
+      (this._scope || this.vm).$[ref] = null
     }
   },
 

+ 5 - 2
test/unit/specs/compiler/compile_spec.js

@@ -8,19 +8,21 @@ var compile = compiler.compile
 if (_.inBrowser) {
   describe('Compile', function () {
 
-    var vm, el, data, directiveTeardown
+    var vm, el, data, directiveBind, directiveTeardown
     beforeEach(function () {
       // We mock vms here so we can assert what the generated
       // linker functions do.
       el = document.createElement('div')
       data = {}
-      directiveTeardown = jasmine.createSpy()
+      directiveBind = jasmine.createSpy('bind')
+      directiveTeardown = jasmine.createSpy('teardown')
       vm = {
         _data: {},
         _directives: [],
         _bindDir: function (name) {
           this._directives.push({
             name: name,
+            _bind: directiveBind,
             _teardown: directiveTeardown
           })
         },
@@ -59,6 +61,7 @@ if (_.inBrowser) {
       var linker = compile(el, options)
       expect(typeof linker).toBe('function')
       linker(vm, el)
+      expect(directiveBind.calls.count()).toBe(4)
       expect(vm._bindDir.calls.count()).toBe(4)
       expect(vm._bindDir).toHaveBeenCalledWith('a', el, descriptorB, defA, undefined, undefined, undefined, undefined)
       expect(vm._bindDir).toHaveBeenCalledWith('a', el.firstChild, descriptorA, defA, undefined, undefined, undefined, undefined)

+ 9 - 1
test/unit/specs/directive_spec.js

@@ -35,6 +35,7 @@ describe('Directive', function () {
       arg: 'someArg',
       filters: [{name: 'test'}]
     }, def)
+    d._bind()
     // properties
     expect(d.el).toBe(el)
     expect(d.name).toBe('test')
@@ -63,6 +64,7 @@ describe('Directive', function () {
     var d = new Directive('test', el, vm, {
       expression: 'a'
     }, def)
+    d._bind()
     expect(d._watcher).toBeUndefined()
     expect(d.expression).toBe('a')
     expect(d.bind).toHaveBeenCalled()
@@ -75,6 +77,7 @@ describe('Directive', function () {
     var d = new Directive('test', el, vm, {
       expression: '{{a}}'
     }, def)
+    d._bind()
     expect(d._watcher).toBeUndefined()
     expect(d.expression).toBe(1)
     expect(d.bind).toHaveBeenCalled()
@@ -87,6 +90,7 @@ describe('Directive', function () {
     var d = new Directive('test', el, vm, {
       expression: '{{a}}'
     }, def)
+    d._bind()
     expect(d._watcher).toBeDefined()
     expect(d.expression).toBe('')
     expect(def.bind).toHaveBeenCalled()
@@ -113,6 +117,7 @@ describe('Directive', function () {
       expression: 'a++',
       filters: [{name: 'test'}]
     }, def)
+    d._bind()
     expect(d._watcher).toBeUndefined()
     expect(d.bind).toHaveBeenCalled()
     var wrappedFn = d.update.calls.argsFor(0)[0]
@@ -133,6 +138,7 @@ describe('Directive', function () {
       expression: 'a',
       filters: [{name: 'test'}]
     }, def)
+    d._bind()
     d.set(2)
     expect(vm.a).toBe(6)
     nextTick(function () {
@@ -144,9 +150,10 @@ describe('Directive', function () {
 
   it('deep', function (done) {
     def.deep = true
-    new Directive('test', el, vm, {
+    var d = new Directive('test', el, vm, {
       expression: 'b'
     }, def)
+    d._bind()
     vm.b.c.d = 3
     nextTick(function () {
       expect(def.update.calls.count()).toBe(2)
@@ -158,6 +165,7 @@ describe('Directive', function () {
     var d = new Directive('test', el, vm, {
       expression: 'a'
     }, def.update)
+    d._bind()
     expect(d.update).toBe(def.update)
     expect(def.update).toHaveBeenCalled()
   })

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

@@ -53,11 +53,11 @@ if (_.inBrowser) {
       })
     })
 
-    it('should be reactive when bound by dynamic component', function (done) {
+    it('should be reactive when bound by dynamic component and hoisted', function (done) {
       var vm = new Vue({
         el: el,
         data: { view: 'one' },
-        template: '<component bind-is="view" ref="test"></component>{{$.test.value}}',
+        template: '{{$.test.value}}<component bind-is="view" ref="test"></component>',
         components: {
           one: {
             id: 'one',

+ 8 - 2
test/unit/specs/element-directives/partial_spec.js

@@ -109,9 +109,15 @@ describe('Partial', function () {
       }
     })
     expect(vm._directives.length).toBe(2)
-    expect(vm._directives[1].name).toBe('partial')
     expect(vm._watchers.length).toBe(2)
-    vm._directives[1]._teardown()
+    var partialDir
+    vm._directives.some(function (d) {
+      if (d.name === 'partial') {
+        partialDir = d
+        return true
+      }
+    })
+    partialDir._teardown()
     // the text-directive should've been removed.
     expect(vm._directives.length).toBe(1)
     expect(vm._directives[0].name).toBe('partial')