- Start Date: 2019-01-14
- Target Major Version: 2.x & 3.x
- Reference Issues: vuejs/vue#7740, vuejs/vue#9180, vuejs/vue#9306
- Implementation PR: (leave this empty)
Introducing a new syntax for scoped slots usage:
slot-props
replacesslot-scope
with slightly different semanticsp;- Shorthand for
slot-props
that can potentially unify the usage of both scoped and normal slots.
Using slot-props
to declare the props passed to the default slot of <foo>
:
<foo slot-props="{ msg }">
{{ msg }}
</foo>
Same example, with shorthand (shorthand symbol is tentative):
<foo ()="{ msg }">
{{ msg }}
</foo>
When we first introduced scoped slots, it was verbose because it required always using <template slot-scope>
:
<foo>
<template slot-scope="{ msg }">
<div>{{ msg }}</div>
</template>
</foo>
To make it less verbose, in 2.5 we introduced the ability to use slot-scope
directly on the slot element:
<foo>
<div slot-scope="{ msg }">
{{ msg }}
</div>
</foo>
This means it works on component as slot as well:
<foo>
<bar slot-scope="{ msg }">
{{ msg }}
</bar>
</foo>
However, the above usage leads to a problem: the placement of slot-scope
doesn't always clearly reflect which component is actually providing the scope variable. Here slot-scope
is placed on the <bar>
component, but it's actually defining a scope variable provided by the default slot of <foo>
.
This gets worse as the nesting deepens:
<foo>
<bar slot-scope="foo">
<baz slot-scope="bar">
<div slot-scope="baz">
{{ foo }} {{ bar }} {{ baz }}
</div>
</baz>
</bar>
</foo>
It's not immediately clear which component is providing which variable in this template.
Someone suggested that we should allow using slot-scope
on a component itself to denote its default slot's scope:
<foo slot-scope="foo">
{{ foo }}
</foo>
Unfortunately, this cannot work as it would lead to ambiguity with component nesting:
<parent>
<foo slot-scope="foo"> <!-- provided by parent or by foo? -->
{{ foo }}
</foo>
</parent>
This is why I now believe allowing using slot-scope
without a template was a mistake.
If we can go back in time, I would probably change the semantics of slot-scope
- but:
-
That would be a breaking change now, and that means we will never be able to ship it in 2.x.
-
Even if we change in in 3.x, changing the semantics of existing syntax can cause a LOT of confusion for future learners that Google into outdated learning materials. We definitely want to avoid that. So, we have to introduce a new attribute to differentiate from
slot-scope
.
-
It can be used on a component to indicate that the component's default slot is a scoped slot, and that props passed to this slot will be available as the variable declared in its attribute value:
<foo slot-props="{ msg }"> {{ msg }} </foo>
-
It can also be used on
<template>
slot containers (exactly the same usage asslot-scope
in this case):<foo> <template slot="header" slot-props="{ msg }"> Header msg: {{ msg }} </template> <template slot="footer" slot-props="{ msg }"> Footer msg: {{ msg }} </template> </foo>
-
It can NOT be used on normal elements. This means for named slots, a
<template>
wrapper for that slot is required.
slot-props
also has a shorthand syntax: ()
(tentative, open to suggestions).
The above examples using shorthand syntax:
<foo ()="{ msg }">
{{ msg }}
</foo>
<foo>
<template slot="header" ()="{ msg }">
Header msg: {{ msg }}
</template>
<template slot="footer" ()="{ msg }">
Footer msg: {{ msg }}
</template>
</foo>
The shorthand syntax can potentially use its attribute name to denote a named slots as well. The above named slots example can thus be written as:
<foo>
<template (header)="{ msg }">
Header msg: {{ msg }}
</template>
<template (footer)="{ msg }">
Footer msg: {{ msg }}
</template>
</foo>
It can even be applied to normal, non-scoped slots, effectively unifying both types of slots in a single syntax:
<foo>
<template (header)>
This is header
</template>
<template (footer)>
This is footer
</template>
</foo>
Considering in v3 we plan to eliminate the conceptual difference between normal vs. scoped slots, this syntax unification seems like a good match.
The tentative shorthand is ()
because it resembles the starting parens of an arrow function and loosely relates to "creating a scope". An arrow function is also typically used for render props, the equivalent of scoped slots in JSX.
Potential drawback: Angular uses ()
in their templates for a different use case. This may confuse users coming from Angular or have to work with both frameworks.
Let's review whether this proposal achieves our goals outlined above:
-
Still provide succinct syntax for most common use cases of scoped slots (single default slot):
<foo ()="{ msg }">{{ msg }}</foo>
-
Clearer connection between scoped variable and the component that is providing it:
Let's take another look at the deep-nesting example using current syntax (
slot-scope
) - notice how slot scope variables provided by<foo>
is declared on<bar>
, and the variable provided by<bar>
is declared on<baz>
...<foo> <bar slot-scope="foo"> <baz slot-scope="bar"> <div slot-scope="baz"> {{ foo }} {{ bar }} {{ baz }} </div> </baz> </bar> </foo>
This is the equivalent using the new syntax:
<foo ()="foo"> <bar ()="bar"> <baz ()="baz"> {{ foo }} {{ bar }} {{ baz }} </baz> </bar> </foo>
Notice that the scope variable provided by a component is also declared on that component itself. The new syntax shows a clearer connection between slot variable declaration and the component providing the variable.
Here are some more usage examples using both the new and old syntax.
-
Introducing a new syntax introduces churn and makes a lot of learning materials covering this topic in the ecosystem outdated. New users may get confused by discovering a new syntax after going through existing tutorials.
- We need good documentation updates on scoped slots to help with this.
-
Some may think this aesthetically is "non-HTML", but we already have shorthands like
:
and@
. Slots is an important component mechanism which I think having its own dedicated shorthand seems justified.
- Directive (
v-scope
) instead of a special attribute as originally proposed in #9180; - Alternative shorthand symbol (
&
) as suggested by @rellect here
The change is fully backwards compatible, so we can roll it out in a minor release (planned for 2.6).
slot-scope
is going to be soft-deprecated: it will be marked deprecated in the docs, and we would encourage everyone to use / switch to the new syntax, but we won't bug you with deprecation messages just yet because we know it's not a top priority for everyone to always migrate to the newest stuff.
In 3.0 we do plan to eventually remove slot-scope
, and only support slot-props
and its shorthand. We will start emitting deprecation messages for slot-scope
usage in the next 2.x minor release to ease the migration to 3.0.
Since this is a pretty well defined syntax change, we can potentially provide a migration tool that can automatically convert your templates to the new syntax.
- Settle on a good shorthand symbol