Przeglądaj źródła

new v-if implementation using partial compilation

Evan You 11 lat temu
rodzic
commit
664e59da1d

+ 2 - 2
src/directives/el.js

@@ -3,11 +3,11 @@ module.exports = {
   isLiteral: true,
 
   bind: function () {
-    this.vm._owner.$$[this.expression] = this.el
+    this.vm.$$[this.expression] = this.el
   },
 
   unbind: function () {
-    this.vm._owner.$$[this.expression] = null
+    delete this.vm.$$[this.expression]
   }
   
 }

+ 40 - 19
src/directives/if.js

@@ -1,5 +1,7 @@
 var _ = require('../util')
+var compile = require('../compile/compile')
 var templateParser = require('../parse/template')
+var transition = require('../transition')
 
 module.exports = {
 
@@ -8,10 +10,16 @@ module.exports = {
     if (!el.__vue__) {
       this.ref = document.createComment('v-if')
       _.replace(el, this.ref)
-      this.inserted = false
-      if (el.tagName === 'TEMPLATE') {
-        this.el = templateParser.parse(el, true)
-      }
+      this.isBlock = el.tagName === 'TEMPLATE'
+      this.template = this.isBlock
+        ? templateParser.parse(el, true)
+        : el
+      // compile the nested partial
+      this.linker = compile(
+        this.template,
+        this.vm.$options,
+        true
+      )
     } else {
       this.invalid = true
       _.warn(
@@ -24,29 +32,42 @@ module.exports = {
   update: function (value) {
     if (this.invalid) return
     if (value) {
-      if (!this.inserted) {
-        if (!this.childVM) {
-          this.childVM = this.vm.$addChild({
-            el: this.el,
-            data: this.vm._data,
-            inherit: true,
-            _anonymous: true
-          })
+      this.insert()
+    } else {
+      this.unbind()
+    }
+  },
+
+  insert: function () {
+    var vm = this.vm
+    var el = templateParser.clone(this.template)
+    var ref = this.ref
+    var decompile = this.linker(vm, el)
+    if (this.isBlock) {
+      var blockStart = el.firstChild
+      this.decompile = function () {
+        decompile()
+        var node = blockStart
+        var next
+        while (node !== ref) {
+          next = node.nextSibling
+          transition.remove(node, vm)
+          node = next
         }
-        this.childVM.$before(this.ref)
-        this.inserted = true
       }
     } else {
-      if (this.inserted) {
-        this.childVM.$remove()
-        this.inserted = false
+      this.decompile = function () {
+        decompile()
+        transition.remove(el, vm)
       }
     }
+    transition.before(el, ref, vm)
   },
 
   unbind: function () {
-    if (this.childVM) {
-      this.childVM.$destroy()
+    if (this.decompile) {
+      this.decompile()
+      this.decompile = null
     }
   }
 

+ 2 - 2
src/directives/ref.js

@@ -11,12 +11,12 @@ module.exports = {
       )
       return
     }
-    this.owner = this.vm.$parent._owner
+    this.owner = this.vm.$parent
     this.owner.$[this.expression] = this.vm
   },
 
   unbind: function () {
-    this.owner.$[this.expression] = null
+    delete this.owner.$[this.expression]
   }
   
 }

+ 3 - 4
src/directives/repeat.js

@@ -64,7 +64,6 @@ module.exports = {
    */
 
   checkRef: function () {
-    this.owner = this.vm._owner
     var childId = _.attr(this.el, 'ref')
     this.childId = childId
       ? this.vm.$interpolate(childId)
@@ -138,10 +137,10 @@ module.exports = {
     this.vms = this.diff(data || [], this.vms)
     // update v-ref
     if (this.childId) {
-      this.owner.$[this.childId] = this.vms
+      this.vm.$[this.childId] = this.vms
     }
     if (this.elId) {
-      this.owner.$$[this.elId] = this.vms.map(function (vm) {
+      this.vm.$$[this.elId] = this.vms.map(function (vm) {
         return vm.$el
       })
     }
@@ -326,7 +325,7 @@ module.exports = {
 
   unbind: function () {
     if (this.childId) {
-      delete this.owner.$[this.childId]
+      delete this.vm.$[this.childId]
     }
     if (this.vms) {
       var i = this.vms.length

+ 0 - 8
src/instance/init.js

@@ -50,14 +50,6 @@ exports._init = function (options) {
   this._childCtors = null  // @type {Object} - hash to cache
                            // child constructors
 
-  // anonymous instances are created by v-if
-  // if an instance is anonymous, its owner will be the
-  // first non-anonymous parent; otherwise its owner will
-  // be itself.
-  this._owner = options._anonymous
-    ? this.$parent._owner
-    : this
-
   // merge options.
   options = this.$options = mergeOptions(
     this.constructor.options,

+ 2 - 12
test/unit/specs/directives/el_spec.js

@@ -17,6 +17,8 @@ if (_.inBrowser) {
       })
       expect(vm.$$.test).toBeTruthy()
       expect(vm.$$.test.id).toBe('test')
+      vm._directives[0]._teardown()
+      expect(vm.$$.test).toBeUndefined()
     })
 
     it('with v-repeat', function (done) {
@@ -36,17 +38,5 @@ if (_.inBrowser) {
       })
     })
 
-    it('inside v-if', function () {
-      var vm = new Vue({
-        el: el,
-        data: { test: true },
-        template: '<div v-if="test"><div id="test" v-el="test"></div></div>'
-      })
-      expect(vm.$$.test).toBeTruthy()
-      expect(vm.$$.test.id).toBe('test')
-      vm._children[0].$destroy()
-      expect(vm.$$.test).toBeNull()
-    })
-
   })
 }

+ 11 - 8
test/unit/specs/directives/if_spec.js

@@ -14,24 +14,30 @@ if (_.inBrowser) {
       var vm = new Vue({
         el: el,
         data: { test: false, a: 'A' },
-        template: '<div v-if="test">{{$data.a}}</div>'
+        template: '<div v-if="test"><div v-component="test"></div></div>',
+        components: {
+          test: {
+            inherit: true,
+            template: '{{a}}'
+          }
+        }
       })
       // lazy instantitation
       expect(el.innerHTML).toBe('<!--v-if-->')
       expect(vm._children).toBeNull()
       vm.test = true
       _.nextTick(function () {
-        expect(el.innerHTML).toBe('<div>A</div><!--v-if-->')
+        expect(el.innerHTML).toBe('<div><div>A</div><!--v-component--></div><!--v-if-->')
         expect(vm._children.length).toBe(1)
         vm.test = false
         _.nextTick(function () {
           expect(el.innerHTML).toBe('<!--v-if-->')
-          expect(vm._children.length).toBe(1)
-          var child = vm._children[0]
+          expect(vm._children.length).toBe(0)
           vm.test = true
           _.nextTick(function () {
-            expect(el.innerHTML).toBe('<div>A</div><!--v-if-->')
+            expect(el.innerHTML).toBe('<div><div>A</div><!--v-component--></div><!--v-if-->')
             expect(vm._children.length).toBe(1)
+            var child = vm._children[0]
             vm.$destroy()
             expect(child._isDestroyed).toBe(true)
             done()
@@ -48,15 +54,12 @@ if (_.inBrowser) {
       })
       // lazy instantitation
       expect(el.innerHTML).toBe('<!--v-if-->')
-      expect(vm._children).toBeNull()
       vm.test = true
       _.nextTick(function () {
         expect(el.innerHTML).toBe('<p>A</p><p>B</p><!--v-if-->')
-        expect(vm._children.length).toBe(1)
         vm.test = false
         _.nextTick(function () {
           expect(el.innerHTML).toBe('<!--v-if-->')
-          expect(vm._children.length).toBe(1)
           done()
         })
       })

+ 1 - 14
test/unit/specs/directives/ref_spec.js

@@ -25,7 +25,7 @@ if (_.inBrowser) {
       expect(vm.$.test).toBeTruthy()
       expect(vm.$.test.$options.id).toBe('test')
       vm.$.test.$destroy()
-      expect(vm.$.test).toBeNull()
+      expect(vm.$.test).toBeUndefined()
     })
 
     it('with v-repeat', function (done) {
@@ -47,19 +47,6 @@ if (_.inBrowser) {
       })
     })
 
-    it('inside v-if', function () {
-      var vm = new Vue({
-        el: el,
-        data: { test: true },
-        components: components,
-        template: '<div v-if="test"><div v-component="test" v-ref="test"></div></div>'
-      })
-      expect(vm.$.test).toBeTruthy()
-      expect(vm.$.test.$options.id).toBe('test')
-      vm.$.test.$destroy()
-      expect(vm.$.test).toBeNull()
-    })
-
     it('nested v-repeat', function () {
       var vm = new Vue({
         el: el,

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

@@ -15,9 +15,6 @@ describe('Instance Init', function () {
   var options = {
     a: 2,
     _anonymous: true,
-    _parent: {
-      _owner: {}
-    },
     el: {}
   }
 
@@ -40,10 +37,6 @@ describe('Instance Init', function () {
     expect(stub.$options.b).toBe(2)
   })
 
-  it('should locate owner', function () {
-    expect(stub._owner).toBe(options._parent._owner)
-  })
-
   it('should call other init methods', function () {
     expect(stub._initEvents).toHaveBeenCalled()
     expect(stub._initScope).toHaveBeenCalled()