feat: new scoped slot syntax implementation update per rfc

This commit is contained in:
Evan You 2019-01-14 22:56:37 -05:00
parent aef5b4e478
commit c5c354d593
3 changed files with 118 additions and 12 deletions

View File

@ -177,6 +177,20 @@ export function getAndRemoveAttr (
return val
}
export function getAndRemoveAttrByRegex (
el: ASTElement,
name: RegExp
) {
const list = el.attrsList
for (let i = 0, l = list.length; i < l; i++) {
const attr = list[i]
if (name.test(attr.name)) {
list.splice(i, 1)
return attr
}
}
}
function rangeSetItem (
item: any,
range?: { start?: number, end?: number }

View File

@ -17,7 +17,8 @@ import {
getBindingAttr,
getAndRemoveAttr,
getRawBindingAttr,
pluckModuleFunction
pluckModuleFunction,
getAndRemoveAttrByRegex
} from '../helpers'
export const onRE = /^@|^v-on:/
@ -31,6 +32,8 @@ export const bindRE = /^:|^\.|^v-bind:/
const propBindRE = /^\./
const modifierRE = /\.[^.]+/g
const scopedSlotShorthandRE = /^:?\(.*\)$/
const lineBreakRE = /[\r\n]/
const whitespaceRE = /\s+/g
@ -568,9 +571,20 @@ function processSlotContent (el) {
getAndRemoveAttr(el, 'slot-scope') ||
// new in 2.6: slot-props and its shorthand works the same as slot-scope
// when used on <template> containers
getAndRemoveAttr(el, 'slot-props') ||
getAndRemoveAttr(el, '()')
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
}
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
@ -585,19 +599,29 @@ function processSlotContent (el) {
el.slotScope = slotScope
} else {
// 2.6: slot-props on component, denotes default slot
slotScope = getAndRemoveAttr(el, 'slot-props') || getAndRemoveAttr(el, '()')
if (slotScope) {
if (process.env.NODE_ENV !== 'production' && !maybeComponent(el)) {
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 slotContainer = slots[`"default"`] = createASTElement('template', [], el)
const target = shorthand ? getScopedSlotShorthandName(shorthand) : `"default"`
const slotContainer = slots[target] = createASTElement('template', [], el)
slotContainer.children = el.children
slotContainer.slotScope = slotScope
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
@ -617,6 +641,14 @@ function processSlotContent (el) {
}
}
function getScopedSlotShorthandName ({ name }) {
return name.charAt(0) === ':'
// dynamic :(name)
? name.slice(2, -1) || `"default"`
// static (name)
: `"${name.slice(1, -1) || `default`}"`
}
// handle <slot/> outlets
function processSlotOutlet (el) {
if (el.tag === 'slot') {

View File

@ -731,5 +731,65 @@ describe('Component scoped slot', () => {
// run tests for both full syntax and shorthand
runSuite('slot-props')
runSuite('()')
it('shorthand named slots', () => {
const vm = new Vue({
template: `
<foo ()="foo">
{{ foo }}
<template (one)="one">
{{ one }}
</template>
<template (two)="two">
{{ two }}
</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`)
})
it('shorthand without scope variable', () => {
const vm = new Vue({
template: `
<foo>
<template (one)>one</template>
<template (two)>two</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)
})
it('shorthand named slots on root', () => {
const vm = new Vue({
template: `
<foo (one)="one">
{{ one }}
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
})
it('dynamic shorthand', () => {
const vm = new Vue({
data: {
a: 'one',
b: 'two'
},
template: `
<foo>
<template :(a)="one">{{ one }} </template>
<template :(b)="two">{{ two }}</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from foo two`)
})
})
})