Преглед изворни кода

simplify slot resolution - re-enable dynamic slot names

Evan You пре 10 година
родитељ
комит
bed322ebc3

+ 1 - 1
src/compiler/index.js

@@ -1,3 +1,3 @@
 export * from './compile'
 export * from './transclude'
-export * from './scan-slots'
+export * from './resolve-slots'

+ 63 - 0
src/compiler/resolve-slots.js

@@ -0,0 +1,63 @@
+import { parseTemplate } from '../parsers/template'
+import {
+  isTemplate,
+  toArray
+} from '../util/index'
+
+/**
+ * Scan and determine slot content distribution.
+ * We do this during transclusion instead at compile time so that
+ * the distribution is decoupled from the compilation order of
+ * the slots.
+ *
+ * @param {Element|DocumentFragment} template
+ * @param {Element} content
+ * @param {Vue} vm
+ */
+
+export function resolveSlots (vm, content) {
+  if (!content) {
+    return
+  }
+  var contents = vm._slotContents = Object.create(null)
+  var el, name
+  for (var i = 0, l = content.children.length; i < l; i++) {
+    el = content.children[i]
+    /* eslint-disable no-cond-assign */
+    if (name = el.getAttribute('slot')) {
+      (contents[name] || (contents[name] = [])).push(el)
+    }
+    /* eslint-enable no-cond-assign */
+  }
+  for (name in contents) {
+    contents[name] = extractFragment(contents[name], content)
+  }
+  if (content.hasChildNodes()) {
+    contents['default'] = extractFragment(content.childNodes, content)
+  }
+}
+
+/**
+ * Extract qualified content nodes from a node list.
+ *
+ * @param {NodeList} nodes
+ * @return {DocumentFragment}
+ */
+
+function extractFragment (nodes, parent) {
+  var frag = document.createDocumentFragment()
+  nodes = toArray(nodes)
+  for (var i = 0, l = nodes.length; i < l; i++) {
+    var node = nodes[i]
+    if (
+      isTemplate(node) &&
+      !node.hasAttribute('v-if') &&
+      !node.hasAttribute('v-for')
+    ) {
+      parent.removeChild(node)
+      node = parseTemplate(node)
+    }
+    frag.appendChild(node)
+  }
+  return frag
+}

+ 0 - 107
src/compiler/scan-slots.js

@@ -1,107 +0,0 @@
-import { parseTemplate } from '../parsers/template'
-import {
-  isTemplate,
-  toArray,
-  getBindAttr,
-  warn,
-  hasNativeTemplate
-} from '../util/index'
-
-/**
- * Scan and determine slot content distribution.
- * We do this during transclusion instead at compile time so that
- * the distribution is decoupled from the compilation order of
- * the slots.
- *
- * @param {Element|DocumentFragment} template
- * @param {Element} content
- * @param {Vue} vm
- */
-
-export function scanSlots (template, content, vm) {
-  if (!content) {
-    return
-  }
-  var contents = vm._slotContents = {}
-  var slots = findSlots(template)
-  if (slots.length) {
-    var hasDefault, slot, name
-    for (var i = 0, l = slots.length; i < l; i++) {
-      slot = slots[i]
-      /* eslint-disable no-cond-assign */
-      if (name = slot.getAttribute('name')) {
-        select(slot, name)
-      } else if (
-        process.env.NODE_ENV !== 'production' &&
-        (name = getBindAttr(slot, 'name'))
-      ) {
-        warn('<slot :name="' + name + '">: slot names cannot be dynamic.')
-      } else {
-        // default slot
-        hasDefault = true
-      }
-      /* eslint-enable no-cond-assign */
-    }
-    if (hasDefault) {
-      contents['default'] = extractFragment(content.childNodes, content)
-    }
-  }
-
-  function select (slot, name) {
-    // named slot
-    var selector = '[slot="' + name + '"]'
-    var nodes = content.querySelectorAll(selector)
-    if (nodes.length) {
-      contents[name] = extractFragment(nodes, content)
-    }
-  }
-}
-
-/**
- * Find all slots in a template, including those nested under
- * a <template> element's content node.
- *
- * @param {Element} el
- * @return {Array|NodeList}
- */
-
-function findSlots (el) {
-  var slots = el.querySelectorAll('slot')
-  /* istanbul ignore if */
-  if (hasNativeTemplate) {
-    slots = toArray(slots)
-    var templates = el.querySelectorAll('template')
-    for (var i = 0; i < templates.length; i++) {
-      slots.push.apply(slots, findSlots(templates[i].content))
-    }
-  }
-  return slots
-}
-
-/**
- * Extract qualified content nodes from a node list.
- *
- * @param {NodeList} nodes
- * @param {Element} parent
- * @return {DocumentFragment}
- */
-
-function extractFragment (nodes, parent) {
-  var frag = document.createDocumentFragment()
-  nodes = toArray(nodes)
-  for (var i = 0, l = nodes.length; i < l; i++) {
-    var node = nodes[i]
-    if (node.parentNode === parent) {
-      if (
-        isTemplate(node) &&
-        !node.hasAttribute('v-if') &&
-        !node.hasAttribute('v-for')
-      ) {
-        parent.removeChild(node)
-        node = parseTemplate(node)
-      }
-      frag.appendChild(node)
-    }
-  }
-  return frag
-}

+ 3 - 4
src/instance/internal/lifecycle.js

@@ -10,7 +10,7 @@ import {
   compile,
   compileRoot,
   transclude,
-  scanSlots
+  resolveSlots
 } from '../../compiler/index'
 
 export default function (Vue) {
@@ -68,9 +68,8 @@ export default function (Vue) {
     var contextOptions = this._context && this._context.$options
     var rootLinker = compileRoot(el, options, contextOptions)
 
-    // scan for slot distribution before compiling the content
-    // so that it's decoupeld from slot/directive compilation order
-    scanSlots(el, options._content, this)
+    // resolve slot distribution
+    resolveSlots(this, options._content)
 
     // compile and link the rest
     var contentLinkFn

+ 0 - 6
src/util/env.js

@@ -8,12 +8,6 @@ export const inBrowser =
   typeof window !== 'undefined' &&
   Object.prototype.toString.call(window) !== '[object Object]'
 
-// Check if the browser supports native <template>.
-export const hasNativeTemplate = (function () {
-  var t = document.createElement('template')
-  return t.content && t.content.nodeType === 11
-})()
-
 // detect devtools
 export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
 

+ 17 - 2
test/unit/specs/directives/element/slot_spec.js

@@ -132,14 +132,14 @@ describe('Slot Distribution', function () {
     expect(el.lastChild.textContent).toBe('fallback c')
   })
 
-  it('should warn expressions in slot names', function () {
+  it('should accept expressions in selectors', function () {
     el.innerHTML = '<p>one</p><p slot="two">two</p>'
     options.template = '<slot :name="theName"></slot>'
     options.data = {
       theName: 'two'
     }
     mount()
-    expect('slot names cannot be dynamic').toHaveBeenWarned()
+    expect(el.innerHTML).toBe('<p slot="two">two</p>')
   })
 
   it('content should be dynamic and compiled in parent scope', function (done) {
@@ -196,6 +196,21 @@ describe('Slot Distribution', function () {
     })
   })
 
+  it('inline v-for', function () {
+    el.innerHTML = '<p slot="1">1</p><p slot="2">2</p><p slot="3">3</p>'
+    new Vue({
+      el: el,
+      template: '<div v-for="n in list"><slot :name="$index + 1"></slot></div>',
+      data: {
+        list: 0
+      },
+      beforeCompile: function () {
+        this.list = this.$options._content.querySelectorAll('p').length
+      }
+    })
+    expect(el.innerHTML).toBe('<div><p slot="1">1</p></div><div><p slot="2">2</p></div><div><p slot="3">3</p></div>')
+  })
+
   it('v-for + component + parent directive + transclusion', function (done) {
     var vm = new Vue({
       el: el,