Просмотр исходного кода

add warning for incorrect <slot> usage (ref #3447)

Evan You 9 лет назад
Родитель
Сommit
7b4a5a14e3
2 измененных файлов с 59 добавлено и 8 удалено
  1. 32 8
      src/compiler/parser/index.js
  2. 27 0
      test/unit/features/component/component-slot.spec.js

+ 32 - 8
src/compiler/parser/index.js

@@ -34,6 +34,7 @@ let preTransforms
 let transforms
 let postTransforms
 let delimiters
+let seenSlots: any
 
 /**
  * Convert HTML string to AST.
@@ -50,6 +51,7 @@ export function parse (
   transforms = pluckModuleFunction(options.modules, 'transformNode')
   postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
   delimiters = options.delimiters
+  seenSlots = Object.create(null)
   const stack = []
   const preserveWhitespace = options.preserveWhitespace !== false
   let root
@@ -265,14 +267,7 @@ function processRef (el) {
   const ref = getBindingAttr(el, 'ref')
   if (ref) {
     el.ref = ref
-    let parent = el
-    while (parent) {
-      if (parent.for !== undefined) {
-        el.refInFor = true
-        break
-      }
-      parent = parent.parent
-    }
+    el.refInFor = checkInFor(el)
   }
 }
 
@@ -331,7 +326,25 @@ function processOnce (el) {
 
 function processSlot (el) {
   if (el.tag === 'slot') {
+    if (process.env.NODE_ENV !== 'production') {
+      if (!el.attrsMap[':name'] && !el.attrsMap['v-bind:name'] && checkInFor(el)) {
+        warn(
+          'Static <slot> found inside v-for: they will not render correctly. ' +
+          'Render the list in parent scope and use a single <slot> instead.'
+        )
+      }
+    }
     el.slotName = getBindingAttr(el, 'name')
+    if (process.env.NODE_ENV !== 'production') {
+      const name = el.slotName
+      if (seenSlots[name]) {
+        warn(
+          `Duplicate ${name ? `<slot> with name ${name}` : `default <slot>`} ` +
+          `found in the same template.`
+        )
+      }
+      seenSlots[name] = true
+    }
   } else {
     const slotTarget = getBindingAttr(el, 'slot')
     if (slotTarget) {
@@ -405,6 +418,17 @@ function processAttrs (el) {
   }
 }
 
+function checkInFor (el: ASTElement): boolean {
+  let parent = el
+  while (parent) {
+    if (parent.for !== undefined) {
+      return true
+    }
+    parent = parent.parent
+  }
+  return false
+}
+
 function parseModifiers (name: string): Object | void {
   const match = name.match(modifierRE)
   if (match) {

+ 27 - 0
test/unit/features/component/component-slot.spec.js

@@ -454,4 +454,31 @@ describe('Component slot', () => {
       expect(calls).toEqual([1, 2, 1, 2])
     }).then(done)
   })
+
+  it('warn duplicate slots', () => {
+    new Vue({
+      template: `<div>
+        <slot></slot><slot></slot>
+        <slot name="a"></slot><slot name="a"></slot>
+      </div>`
+    }).$mount()
+    expect('Duplicate default <slot>').toHaveBeenWarned()
+    expect('Duplicate <slot> with name "a"').toHaveBeenWarned()
+  })
+
+  it('warn static slot inside v-for', () => {
+    new Vue({
+      template: `<div>
+        <div v-for="i in 1"><slot :name="'test' + i"></slot></div>
+      </div>`
+    }).$mount()
+    expect('Static <slot> found inside v-for').not.toHaveBeenWarned()
+
+    new Vue({
+      template: `<div>
+        <div v-for="i in 1"><slot></slot></div>
+      </div>`
+    }).$mount()
+    expect('Static <slot> found inside v-for').toHaveBeenWarned()
+  })
 })