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

feat: update new slot syntax per RFC revision

Evan You 7 лет назад
Родитель
Сommit
4fca0454bd
2 измененных файлов с 91 добавлено и 77 удалено
  1. 60 58
      src/compiler/parser/index.js
  2. 31 19
      test/unit/features/component/component-scoped-slot.spec.js

+ 60 - 58
src/compiler/parser/index.js

@@ -26,13 +26,14 @@ export const dirRE = /^v-|^@|^:|^\./
 export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
 export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
 const stripParensRE = /^\(|\)$/g
+const dynamicKeyRE = /^\[.*\]$/
 
 const argRE = /:(.*)$/
 export const bindRE = /^:|^\.|^v-bind:/
 const propBindRE = /^\./
 const modifierRE = /\.[^.]+/g
 
-const scopedSlotShorthandRE = /^:?\(.*\)$/
+const slotRE = /^v-slot(:|$)|^#/
 
 const lineBreakRE = /[\r\n]/
 const whitespaceRE = /\s+/g
@@ -566,27 +567,7 @@ function processSlotContent (el) {
         true
       )
     }
-    el.slotScope = (
-      slotScope ||
-      getAndRemoveAttr(el, 'slot-scope')
-    )
-    if (process.env.NEW_SLOT_SYNTAX) {
-      // new in 2.6: slot-props and its shorthand works the same as slot-scope
-      // when used on <template> containers
-      el.slotScope = el.slotScope || getAndRemoveAttr(el, 'slot-props')
-      // 2.6 shorthand syntax
-      const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
-      if (shorthand) {
-        if (process.env.NODE_ENV !== 'production' && el.slotScope) {
-          warn(
-            `Unexpected mixed usage of different slot syntaxes.`,
-            el
-          )
-        }
-        el.slotTarget = getScopedSlotShorthandName(shorthand)
-        el.slotScope = shorthand.value
-      }
-    }
+    el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
   } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
     /* istanbul ignore if */
     if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
@@ -599,36 +580,6 @@ function processSlotContent (el) {
       )
     }
     el.slotScope = slotScope
-  } else if (process.env.NEW_SLOT_SYNTAX) {
-    // 2.6: slot-props on component, denotes default slot
-    slotScope = getAndRemoveAttr(el, 'slot-props')
-    const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
-    if (slotScope || shorthand) {
-      if (process.env.NODE_ENV !== 'production') {
-        if (!maybeComponent(el)) {
-          warn(
-            `slot-props cannot be used on non-component elements.`,
-            el.rawAttrsMap['slot-props'] || el.rawAttrsMap['()']
-          )
-        }
-        if (slotScope && shorthand) {
-          warn(
-            `Unexpected mixed usage of different slot syntaxes.`,
-            el
-          )
-        }
-      }
-      // add the component's children to its default slot
-      const slots = el.scopedSlots || (el.scopedSlots = {})
-      const target = shorthand ? getScopedSlotShorthandName(shorthand) : `"default"`
-      const slotContainer = slots[target] = createASTElement('template', [], el)
-      slotContainer.children = el.children
-      slotContainer.slotScope = shorthand ? shorthand.value : slotScope
-      // remove children as they are returned from scopedSlots now
-      el.children = []
-      // mark el non-plain so data gets generated
-      el.plain = false
-    }
   }
 
   // slot="xxx"
@@ -641,14 +592,65 @@ function processSlotContent (el) {
       addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))
     }
   }
+
+  // 2.6 v-slot syntax
+  if (process.env.NEW_SLOT_SYNTAX) {
+    if (el.tag === 'template') {
+      // v-slot on <template>
+      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
+      if (slotBinding) {
+        if (
+          process.env.NODE_ENV !== 'production' &&
+          (el.slotTarget || el.slotScope)
+        ) {
+          warn(
+            `Unexpected mixed usage of different slot syntaxes.`,
+            el
+          )
+        }
+        el.slotTarget = getSlotName(slotBinding)
+        el.slotScope = slotBinding.value
+      }
+    } else {
+      // v-slot on component, denotes default slot
+      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
+      if (slotBinding) {
+        if (process.env.NODE_ENV !== 'production') {
+          if (!maybeComponent(el)) {
+            warn(
+              `v-slot cannot be used on non-component elements.`,
+              slotBinding
+            )
+          }
+          if (el.slotScope || el.slotTarget) {
+            warn(
+              `Unexpected mixed usage of different slot syntaxes.`,
+              el
+            )
+          }
+        }
+        // add the component's children to its default slot
+        const slots = el.scopedSlots || (el.scopedSlots = {})
+        const target = getSlotName(slotBinding)
+        const slotContainer = slots[target] = createASTElement('template', [], el)
+        slotContainer.children = el.children
+        slotContainer.slotScope = slotBinding.value
+        // remove children as they are returned from scopedSlots now
+        el.children = []
+        // mark el non-plain so data gets generated
+        el.plain = false
+      }
+    }
+  }
 }
 
-function getScopedSlotShorthandName ({ name }) {
-  return name.charAt(0) === ':'
-    // dynamic :(name)
-    ? name.slice(2, -1) || `"default"`
-    // static (name)
-    : `"${name.slice(1, -1) || `default`}"`
+function getSlotName ({ name }) {
+  name = name.replace(slotRE, '')
+  return dynamicKeyRE.test(name)
+    // dynamic [name]
+    ? name.slice(1, -1)
+    // static name
+    : `"${name || `default`}"`
 }
 
 // handle <slot/> outlets

+ 31 - 19
test/unit/features/component/component-scoped-slot.spec.js

@@ -634,7 +634,7 @@ describe('Component scoped slot', () => {
 
   // 2.6 new slot syntax
   if (process.env.NEW_SLOT_SYNTAX) {
-    describe('slot-props syntax', () => {
+    describe('v-slot syntax', () => {
       const Foo = {
         render(h) {
           return h('div', [
@@ -657,6 +657,10 @@ describe('Component scoped slot', () => {
         }
       }
 
+      const toNamed = (syntax, name) => syntax.length === 1
+        ? syntax + name // shorthand
+        : syntax + ':' + name // full syntax
+
       function runSuite(syntax) {
         it('default slot', () => {
           const vm = new Vue({
@@ -685,12 +689,12 @@ describe('Component scoped slot', () => {
         it('default + named slots', () => {
           const vm = new Vue({
             template: `
-              <foo ()="foo">
+              <foo #="foo">
                 {{ foo }}
-                <template slot="one" ${syntax}="one">
+                <template ${toNamed(syntax, 'one')}="one">
                   {{ one }}
                 </template>
-                <template slot="two" ${syntax}="two">
+                <template ${toNamed(syntax, 'two')}="two">
                   {{ two }}
                 </template>
               </foo>
@@ -704,12 +708,12 @@ describe('Component scoped slot', () => {
           const vm = new Vue({
             template: `
               <foo>
-                <template slot="one" ${syntax}="one">
+                <template ${toNamed(syntax, 'one')}="one">
                   <bar ${syntax}="bar">
                     {{ one }} {{ bar }}
                   </bar>
                 </template>
-                <template slot="two" ${syntax}="two">
+                <template ${toNamed(syntax, 'two')}="two">
                   <baz ${syntax}="baz">
                     {{ two }} {{ baz }}
                   </baz>
@@ -721,27 +725,35 @@ describe('Component scoped slot', () => {
           expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from bar from foo two from baz`)
         })
 
-        it('should warn slot-props usage on non-component elements', () => {
+        it('should warn v-slot usage on non-component elements', () => {
           const vm = new Vue({
             template: `<div ${syntax}="foo"/>`
           }).$mount()
-          expect(`slot-props cannot be used on non-component elements`).toHaveBeenWarned()
+          expect(`v-slot cannot be used on non-component elements`).toHaveBeenWarned()
+        })
+
+        it('should warn mixed usage', () => {
+          const vm = new Vue({
+            template: `<foo><bar slot="one" slot-scope="bar" ${syntax}="bar"></bar></foo>`,
+            components: { Foo, Bar }
+          }).$mount()
+          expect(`Unexpected mixed usage of different slot syntaxes`).toHaveBeenWarned()
         })
       }
 
       // run tests for both full syntax and shorthand
-      runSuite('slot-props')
-      runSuite('()')
+      runSuite('v-slot')
+      runSuite('#')
 
       it('shorthand named slots', () => {
         const vm = new Vue({
           template: `
-            <foo ()="foo">
+            <foo #="foo">
               {{ foo }}
-              <template (one)="one">
+              <template #one="one">
                 {{ one }}
               </template>
-              <template (two)="two">
+              <template #two="two">
                 {{ two }}
               </template>
             </foo>
@@ -755,8 +767,8 @@ describe('Component scoped slot', () => {
         const vm = new Vue({
           template: `
             <foo>
-              <template (one)>one</template>
-              <template (two)>two</template>
+              <template #one>one</template>
+              <template #two>two</template>
             </foo>
           `,
           components: { Foo }
@@ -767,7 +779,7 @@ describe('Component scoped slot', () => {
       it('shorthand named slots on root', () => {
         const vm = new Vue({
           template: `
-            <foo (one)="one">
+            <foo #one="one">
               {{ one }}
             </foo>
           `,
@@ -776,7 +788,7 @@ describe('Component scoped slot', () => {
         expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
       })
 
-      it('dynamic shorthand', () => {
+      it('dynamic slot name', () => {
         const vm = new Vue({
           data: {
             a: 'one',
@@ -784,8 +796,8 @@ describe('Component scoped slot', () => {
           },
           template: `
             <foo>
-              <template :(a)="one">{{ one }} </template>
-              <template :(b)="two">{{ two }}</template>
+              <template #[a]="one">{{ one }} </template>
+              <template v-slot:[b]="two">{{ two }}</template>
             </foo>
           `,
           components: { Foo }