Skip to content

Commit 4fca045

Browse files
committed
feat: update new slot syntax per RFC revision
1 parent a47a0ee commit 4fca045

File tree

2 files changed

+91
-77
lines changed

2 files changed

+91
-77
lines changed

src/compiler/parser/index.js

+60-58
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ export const dirRE = /^v-|^@|^:|^\./
2626
export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
2727
export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
2828
const stripParensRE = /^\(|\)$/g
29+
const dynamicKeyRE = /^\[.*\]$/
2930

3031
const argRE = /:(.*)$/
3132
export const bindRE = /^:|^\.|^v-bind:/
3233
const propBindRE = /^\./
3334
const modifierRE = /\.[^.]+/g
3435

35-
const scopedSlotShorthandRE = /^:?\(.*\)$/
36+
const slotRE = /^v-slot(:|$)|^#/
3637

3738
const lineBreakRE = /[\r\n]/
3839
const whitespaceRE = /\s+/g
@@ -566,27 +567,7 @@ function processSlotContent (el) {
566567
true
567568
)
568569
}
569-
el.slotScope = (
570-
slotScope ||
571-
getAndRemoveAttr(el, 'slot-scope')
572-
)
573-
if (process.env.NEW_SLOT_SYNTAX) {
574-
// new in 2.6: slot-props and its shorthand works the same as slot-scope
575-
// when used on <template> containers
576-
el.slotScope = el.slotScope || getAndRemoveAttr(el, 'slot-props')
577-
// 2.6 shorthand syntax
578-
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
579-
if (shorthand) {
580-
if (process.env.NODE_ENV !== 'production' && el.slotScope) {
581-
warn(
582-
`Unexpected mixed usage of different slot syntaxes.`,
583-
el
584-
)
585-
}
586-
el.slotTarget = getScopedSlotShorthandName(shorthand)
587-
el.slotScope = shorthand.value
588-
}
589-
}
570+
el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
590571
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
591572
/* istanbul ignore if */
592573
if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
@@ -599,36 +580,6 @@ function processSlotContent (el) {
599580
)
600581
}
601582
el.slotScope = slotScope
602-
} else if (process.env.NEW_SLOT_SYNTAX) {
603-
// 2.6: slot-props on component, denotes default slot
604-
slotScope = getAndRemoveAttr(el, 'slot-props')
605-
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
606-
if (slotScope || shorthand) {
607-
if (process.env.NODE_ENV !== 'production') {
608-
if (!maybeComponent(el)) {
609-
warn(
610-
`slot-props cannot be used on non-component elements.`,
611-
el.rawAttrsMap['slot-props'] || el.rawAttrsMap['()']
612-
)
613-
}
614-
if (slotScope && shorthand) {
615-
warn(
616-
`Unexpected mixed usage of different slot syntaxes.`,
617-
el
618-
)
619-
}
620-
}
621-
// add the component's children to its default slot
622-
const slots = el.scopedSlots || (el.scopedSlots = {})
623-
const target = shorthand ? getScopedSlotShorthandName(shorthand) : `"default"`
624-
const slotContainer = slots[target] = createASTElement('template', [], el)
625-
slotContainer.children = el.children
626-
slotContainer.slotScope = shorthand ? shorthand.value : slotScope
627-
// remove children as they are returned from scopedSlots now
628-
el.children = []
629-
// mark el non-plain so data gets generated
630-
el.plain = false
631-
}
632583
}
633584

634585
// slot="xxx"
@@ -641,14 +592,65 @@ function processSlotContent (el) {
641592
addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))
642593
}
643594
}
595+
596+
// 2.6 v-slot syntax
597+
if (process.env.NEW_SLOT_SYNTAX) {
598+
if (el.tag === 'template') {
599+
// v-slot on <template>
600+
const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
601+
if (slotBinding) {
602+
if (
603+
process.env.NODE_ENV !== 'production' &&
604+
(el.slotTarget || el.slotScope)
605+
) {
606+
warn(
607+
`Unexpected mixed usage of different slot syntaxes.`,
608+
el
609+
)
610+
}
611+
el.slotTarget = getSlotName(slotBinding)
612+
el.slotScope = slotBinding.value
613+
}
614+
} else {
615+
// v-slot on component, denotes default slot
616+
const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
617+
if (slotBinding) {
618+
if (process.env.NODE_ENV !== 'production') {
619+
if (!maybeComponent(el)) {
620+
warn(
621+
`v-slot cannot be used on non-component elements.`,
622+
slotBinding
623+
)
624+
}
625+
if (el.slotScope || el.slotTarget) {
626+
warn(
627+
`Unexpected mixed usage of different slot syntaxes.`,
628+
el
629+
)
630+
}
631+
}
632+
// add the component's children to its default slot
633+
const slots = el.scopedSlots || (el.scopedSlots = {})
634+
const target = getSlotName(slotBinding)
635+
const slotContainer = slots[target] = createASTElement('template', [], el)
636+
slotContainer.children = el.children
637+
slotContainer.slotScope = slotBinding.value
638+
// remove children as they are returned from scopedSlots now
639+
el.children = []
640+
// mark el non-plain so data gets generated
641+
el.plain = false
642+
}
643+
}
644+
}
644645
}
645646

646-
function getScopedSlotShorthandName ({ name }) {
647-
return name.charAt(0) === ':'
648-
// dynamic :(name)
649-
? name.slice(2, -1) || `"default"`
650-
// static (name)
651-
: `"${name.slice(1, -1) || `default`}"`
647+
function getSlotName ({ name }) {
648+
name = name.replace(slotRE, '')
649+
return dynamicKeyRE.test(name)
650+
// dynamic [name]
651+
? name.slice(1, -1)
652+
// static name
653+
: `"${name || `default`}"`
652654
}
653655

654656
// handle <slot/> outlets

test/unit/features/component/component-scoped-slot.spec.js

+31-19
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ describe('Component scoped slot', () => {
634634

635635
// 2.6 new slot syntax
636636
if (process.env.NEW_SLOT_SYNTAX) {
637-
describe('slot-props syntax', () => {
637+
describe('v-slot syntax', () => {
638638
const Foo = {
639639
render(h) {
640640
return h('div', [
@@ -657,6 +657,10 @@ describe('Component scoped slot', () => {
657657
}
658658
}
659659

660+
const toNamed = (syntax, name) => syntax.length === 1
661+
? syntax + name // shorthand
662+
: syntax + ':' + name // full syntax
663+
660664
function runSuite(syntax) {
661665
it('default slot', () => {
662666
const vm = new Vue({
@@ -685,12 +689,12 @@ describe('Component scoped slot', () => {
685689
it('default + named slots', () => {
686690
const vm = new Vue({
687691
template: `
688-
<foo ()="foo">
692+
<foo #="foo">
689693
{{ foo }}
690-
<template slot="one" ${syntax}="one">
694+
<template ${toNamed(syntax, 'one')}="one">
691695
{{ one }}
692696
</template>
693-
<template slot="two" ${syntax}="two">
697+
<template ${toNamed(syntax, 'two')}="two">
694698
{{ two }}
695699
</template>
696700
</foo>
@@ -704,12 +708,12 @@ describe('Component scoped slot', () => {
704708
const vm = new Vue({
705709
template: `
706710
<foo>
707-
<template slot="one" ${syntax}="one">
711+
<template ${toNamed(syntax, 'one')}="one">
708712
<bar ${syntax}="bar">
709713
{{ one }} {{ bar }}
710714
</bar>
711715
</template>
712-
<template slot="two" ${syntax}="two">
716+
<template ${toNamed(syntax, 'two')}="two">
713717
<baz ${syntax}="baz">
714718
{{ two }} {{ baz }}
715719
</baz>
@@ -721,27 +725,35 @@ describe('Component scoped slot', () => {
721725
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from bar from foo two from baz`)
722726
})
723727

724-
it('should warn slot-props usage on non-component elements', () => {
728+
it('should warn v-slot usage on non-component elements', () => {
725729
const vm = new Vue({
726730
template: `<div ${syntax}="foo"/>`
727731
}).$mount()
728-
expect(`slot-props cannot be used on non-component elements`).toHaveBeenWarned()
732+
expect(`v-slot cannot be used on non-component elements`).toHaveBeenWarned()
733+
})
734+
735+
it('should warn mixed usage', () => {
736+
const vm = new Vue({
737+
template: `<foo><bar slot="one" slot-scope="bar" ${syntax}="bar"></bar></foo>`,
738+
components: { Foo, Bar }
739+
}).$mount()
740+
expect(`Unexpected mixed usage of different slot syntaxes`).toHaveBeenWarned()
729741
})
730742
}
731743

732744
// run tests for both full syntax and shorthand
733-
runSuite('slot-props')
734-
runSuite('()')
745+
runSuite('v-slot')
746+
runSuite('#')
735747

736748
it('shorthand named slots', () => {
737749
const vm = new Vue({
738750
template: `
739-
<foo ()="foo">
751+
<foo #="foo">
740752
{{ foo }}
741-
<template (one)="one">
753+
<template #one="one">
742754
{{ one }}
743755
</template>
744-
<template (two)="two">
756+
<template #two="two">
745757
{{ two }}
746758
</template>
747759
</foo>
@@ -755,8 +767,8 @@ describe('Component scoped slot', () => {
755767
const vm = new Vue({
756768
template: `
757769
<foo>
758-
<template (one)>one</template>
759-
<template (two)>two</template>
770+
<template #one>one</template>
771+
<template #two>two</template>
760772
</foo>
761773
`,
762774
components: { Foo }
@@ -767,7 +779,7 @@ describe('Component scoped slot', () => {
767779
it('shorthand named slots on root', () => {
768780
const vm = new Vue({
769781
template: `
770-
<foo (one)="one">
782+
<foo #one="one">
771783
{{ one }}
772784
</foo>
773785
`,
@@ -776,16 +788,16 @@ describe('Component scoped slot', () => {
776788
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
777789
})
778790

779-
it('dynamic shorthand', () => {
791+
it('dynamic slot name', () => {
780792
const vm = new Vue({
781793
data: {
782794
a: 'one',
783795
b: 'two'
784796
},
785797
template: `
786798
<foo>
787-
<template :(a)="one">{{ one }} </template>
788-
<template :(b)="two">{{ two }}</template>
799+
<template #[a]="one">{{ one }} </template>
800+
<template v-slot:[b]="two">{{ two }}</template>
789801
</foo>
790802
`,
791803
components: { Foo }

0 commit comments

Comments
 (0)