Description
This is an update of #9180 where we attempt to finalize a new syntax proposal for scoped slots (in a backwards compatible way).
Rationale
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.
In 2.6, we are planning to introduce a new syntax for scoped slots.
Goals of the new proposal
-
Still provide succinct syntax for most common use cases of scoped slots (default slots)
-
Clearer connection between scoped variable and the component that is providing it.
Syntax Details
-
Introducing a new special attribute:
slot-props
.-
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 }"> {{ msg }} </template> </foo>
-
It can NOT be used on normal elements.
-
-
slot-props
also has a shorthand syntax:()
.The above examples using shorthand syntax:
<foo ()="{ msg }"> {{ msg }} </foo> <foo> <template slot="header" ()="{ msg }"> {{ msg }} </template> </foo>
The 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.
Comparison: New vs. Old
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):
Can we get any more succinct than this?
<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.
Q&A
Why a new attribute instead of fixing slot-scope
?
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
.
What happens to slot-scope
?
It's 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.