Browse Source

use fragment in partials

Evan You 10 years ago
parent
commit
600ad90eea

+ 24 - 31
src/directives/if.js

@@ -22,30 +22,30 @@ module.exports = {
     if (this.invalid) return
     if (value) {
       if (!this.frag) {
-        this.create()
+        this.insert()
       }
     } else {
-      this.teardown()
+      this.remove()
     }
   },
 
-  create: function () {
+  insert: function () {
     this.frag = this.factory.create(this._host, this._scope)
     this.frag.before(this.anchor)
     // call attached for all the child components created
     // during the compilation
     if (_.inDoc(this.vm.$el)) {
-      var children = this.getContainedComponents()
+      var children = getContainedComponents(this.vm, this.frag.node, this.anchor)
       if (children) children.forEach(callAttach)
     }
   },
 
-  teardown: function () {
+  remove: function () {
     if (!this.frag) return
     // collect children beforehand
     var children
     if (_.inDoc(this.vm.$el)) {
-      children = this.getContainedComponents()
+      children = getContainedComponents(this.vm, this.frag.node, this.anchor)
     }
     this.frag.remove()
     if (children) children.forEach(callDetach)
@@ -53,31 +53,6 @@ module.exports = {
     this.frag = null
   },
 
-  getContainedComponents: function () {
-    var vm = this.vm
-    var start = this.frag.node
-    var end = this.anchor
-
-    function contains (c) {
-      var cur = start
-      var next
-      while (next !== end) {
-        next = cur.nextSibling
-        if (
-          cur === c.$el ||
-          cur.contains && cur.contains(c.$el)
-        ) {
-          return true
-        }
-        cur = next
-      }
-      return false
-    }
-
-    return vm.$children.length &&
-      vm.$children.filter(contains)
-  },
-
   unbind: function () {
     if (this.frag) {
       this.frag.unlink()
@@ -85,6 +60,24 @@ module.exports = {
   }
 }
 
+function getContainedComponents (vm, start, end) {
+  return vm.$children.length && vm.$children.filter(function (c) {
+    var cur = start
+    var next
+    while (next !== end) {
+      next = cur.nextSibling
+      if (
+        cur === c.$el ||
+        cur.contains && cur.contains(c.$el)
+      ) {
+        return true
+      }
+      cur = next
+    }
+    return false
+  })
+}
+
 function callAttach (child) {
   if (!child._isAttached) {
     child._callHook('attached')

+ 9 - 32
src/element-directives/partial.js

@@ -1,25 +1,13 @@
 var _ = require('../util')
-var templateParser = require('../parsers/template')
-var textParser = require('../parsers/text')
-var compiler = require('../compiler')
-var Cache = require('../cache')
-var cache = new Cache(1000)
-
-// v-partial reuses logic from v-if
+var FragmentFactory = require('../fragment/factory')
 var vIf = require('../directives/if')
 
 module.exports = {
 
-  link: vIf.link,
-  teardown: vIf.teardown,
-  getContainedComponents: vIf.getContainedComponents,
-
   bind: function () {
     var el = this.el
-    this.start = _.createAnchor('v-partial-start')
-    this.end = _.createAnchor('v-partial-end')
-    _.replace(el, this.end)
-    _.before(this.start, this.end)
+    this.anchor = _.createAnchor('v-partial')
+    _.replace(el, this.anchor)
     var id = el.getAttribute('name')
     var tokens = textParser.parse(id)
     if (tokens) {
@@ -35,11 +23,12 @@ module.exports = {
     var self = this
     var exp = textParser.tokensToExp(tokens)
     this.unwatch = this.vm.$watch(exp, function (value) {
-      self.teardown()
+      vIf.remove.call(self)
       self.insert(value)
     }, {
       immediate: true,
-      user: false
+      user: false,
+      scope: this._scope
     })
   },
 
@@ -49,25 +38,13 @@ module.exports = {
       _.assertAsset(partial, 'partial', id)
     }
     if (partial) {
-      var frag = templateParser.parse(partial, true)
-      // cache partials based on constructor id.
-      var cacheId = (this.vm.constructor.cid || '') + partial
-      var linker = this.compile(frag, cacheId)
-      // this is provided by v-if
-      this.link(frag, linker)
+      this.factory = new FragmentFactory(this.vm, partial)
+      vIf.insert.call(this)
     }
   },
 
-  compile: function (frag, cacheId) {
-    var hit = cache.get(cacheId)
-    if (hit) return hit
-    var linker = compiler.compile(frag, this.vm.$options, true)
-    cache.put(cacheId, linker)
-    return linker
-  },
-
   unbind: function () {
-    if (this.unlink) this.unlink()
+    if (this.frag) this.frag.unlink()
     if (this.unwatch) this.unwatch()
   }
 }

+ 6 - 5
src/fragment/factory.js

@@ -11,24 +11,25 @@ var linkerCache = new Cache(5000)
  * fragment. Caches the compiled linker if possible.
  *
  * @param {Vue} vm
- * @param {Element} el
+ * @param {Element|String} el
  */
 
 function FragmentFactory (vm, el) {
   this.vm = vm
   var template
-  if (!_.isTemplate(el)) {
+  var isString = typeof el === 'string'
+  if (isString || _.isTemplate(el)) {
+    template = templateParser.parse(el, true)
+  } else {
     template = document.createDocumentFragment()
     template.appendChild(el)
-  } else {
-    template = templateParser.parse(el, true)
   }
   this.template = template
   // linker can be cached, but only for components
   var linker
   var cid = vm.constructor.cid
   if (cid > 0) {
-    var cacheId = cid + el.outerHTML
+    var cacheId = cid + (isString ? el : el.outerHTML)
     linker = linkerCache.get(cacheId)
     if (!linker) {
       linker = compiler.compile(template, vm.$options, true)

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

@@ -55,18 +55,21 @@ describe('Partial', function () {
       calls++
       return compile.apply(this, arguments)
     }
-    new Vue({
-      el: el,
+    // Note: caching only works on components, not native Vue
+    var Comp = Vue.extend({
       template:
         '<partial name="cache-test"></partial> ' +
         '<partial name="cache-test"></partial>',
-      data: {
-        msg: 'hi'
-      },
       partials: {
         'cache-test': 'cache-test {{msg}}'
       }
     })
+    new Comp({
+      el: el,
+      data: {
+        msg: 'hi'
+      }
+    })
     expect(el.textContent).toBe('cache-test hi cache-test hi')
     // one call for instance, and one for partial
     expect(calls).toBe(2)