question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Abandoned] RFC: Simplify scoped slot usage

See original GitHub issue

This is a follow up of https://github.com/vuejs/vue/issues/7740#issuecomment-371309357

Rational

Problems with current scoped slot usage:

  • Verbose if using <template slot-scope>
  • Limited to one element/component is using slot-scope directly on the slot element.

Proposal

Introduce a new v-scope directive, that can only be used on components:

<comp v-scope="scope">
  {{ scope.msg }}
</comp>

It would work the same as slot-scope for the default scoped slot for <comp> (with <comp> providing the scope value). So it also works with deconstructing:

<comp v-scope="{ msg }">
  {{ msg }}
</comp>

Why a New Directive

I believe the team briefly discussed what I proposed in https://github.com/vuejs/vue/issues/7740#issuecomment-371309357 on Slack some time back, but I could no longer find the chat record. Here’s the reasoning behind a new directive:

  • slot-scope was introduced as a special attribute instead of a directive (attributes that start with v- prefix) because slot is an attribute, and we wanted to keep slot-related attributes consistent. slot was in turn introduced as a non-directive attribute because we want the usage to mirror the actual slot usage in the Shadow DOM standard. We figured it would be best to avoid having our own parallel v-slot when there’s something that is conceptually the same in the standard.

  • Originally, slot-scope was designed to be only usable on <template> elements that act as abstract containers. But that was verbose - so we introduced the ability to use it directly on a slot element without the wrapping <template>. However, this also makes it impossible to allow using slot-scope directly on the component itself, because it would lead to ambiguity as illustrated here.

  • I thought about adding modifiers or special prefixes to slot-scope so that that we can use it on a component directly to indicate its slot content should be treated as the default scoped slot, but neither a modifier or a prefix like $ seem to be the right fit. The modifier by design should only be applied to directives, while new special syntax for a single use case is inconsistent with the whole syntax design.

  • For a very long time we’ve shied away from adding more directives, part of it being template syntax is something we want to keep as stable as possible, part of it being that we want to keep core directives to a minimum and only do things that users cannot easily do in userland. However, in this case scoped slot usage is important enough, and I think a new directive can be justified for making its usage significantly less noisy.

Concerns

  • The expression accepted by v-scope is different from most other directives: it expects a temporary variable name (which can also be a deconstruction), but not without a precedence: it acts just like the alias part of v-for. So conceptually v-scope falls into the same camp with v-for as a structural directive that creates temporary variables for its inner scope.

  • This would break the users code if the user has a custom directive named v-scope and is used on a component.

    • Since custom directives in v2 are primarily focused on direct DOM manipulations, it’s relatively rare to see custom directives used on components, even more so for something that happens to be called v-scope, so the impact should be minimal.

    • Even in the case of it actually happening, it is straightforward to deal with by simply renaming the custom directive.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:27
  • Comments:36 (33 by maintainers)

github_iconTop GitHub Comments

11reactions
chrisvfritzcommented, Dec 13, 2018

Breaking down the problem

Trying to synthesize, it sounds like we want a solution that will:

  • reduce boilerplate needed to access data from scoped slots
  • avoid implying that data for the default slot is available in named slots
  • minimize the number of new APIs we have to introduce
  • don’t reinvent APIs where web components spec already has an acceptable solution
  • stay explicit, to avoid confusion about where data came from

I might have a solution that addresses all of these! 🤞 With $event for v-on, we have a precedent of providing expressions run inside an implicit function with a named argument. People seem to enjoy that convenience and I don’t see a lot of confusion caused by it, so maybe we should follow that example for scoped slots.

Proposed solution

Instead of using v-scope, we could make the slot scope available as $slot. For example:

<TodoList :todos="todos">
  {{ $slot.todo.text }}

  <template slot="metaBar">
    You have {{ $slot.totals.incomplete }} todos left.
  </template>
</TodoList>

For context, the child template might look like:

<ul>
  <li v-for="todo in todos">
    <slot :todo="todo" />
  </li>
</ul>

<div>
  <slot name="metaBar" v-bind="itemsStats" />
</div>

When slots are nested, the $slot object would merge slot data, with the inner-most slots taking override priority. For example, in:

<Outer>
  <Middle>
    <Inner>
      {{ $slot }}
    </Inner>
  </Middle>
</Outer>

merging might look like this:

$slot = {
  ...outerSlotData,
  ...middleSlotData,
  ...innerSlotData
}

You may be worrying/wondering about how to handle namespace overlap and in 99.9% of cases, I really don’t think it’ll be an issue. For example:

<UserData id="chrisvfritz">
  My email is {{ $slot.user.email }}, but you can reach Sarah at
  <UserData id="sdras">
    {{ $slot.user.email }}
  </UserData>.
</UserData>

In the above example, $slot.user will always have the correct value, despite the nesting scopes. However, sometimes you’ll really need access to both properties at the same time, like in:

<UserData id="chrisvfritz">
  <template slot-scope="me">
    <UserData id="sdras">
      <template slot-scope="sarah">
        <CollaborationPageLink :user-ids="[me.user.id, sarah.user.id]">
          View your collaboration history
        </CollaborationPageLink>
      </template>
    </UserData>
  </template>
</UserData>

For these rare edge cases, users could still use slot-scope with its current behavior as an escape hatch. But v-scope would still be unnecessary, because if I understand correctly, its purpose would be to simplify the most common use cases, which the $slot object would already have accomplished without causing the same problems.

Advantages

  • When a user wants to access data provided by a slot, the only boilerplate is $slot, which is about as brief as it can be while remaining explicit. It’s immediately obvious that data came from a slot, without needing to search through the component to see where a property is defined.

  • To know which data they can access, users only have to think about which slot(s) the content will be rendered to. I think with v-scope, there would be a lot of confusion with people assuming it works like v-for, because that’s the only other directive that defines scoped context properties.

  • Users don’t have to be familiar and comfortable with destructuring to use scoped slots, thus reducing the learning curve.

  • In some cases, especially with many nested slots that all provide state, it won’t be obvious which component some state came from and users will have to reach slot-scope again. This might sound like a disadvantage, but when I see nested scoped slots, it’s almost always for the state provider pattern, which I strongly feel is an anti-pattern. Here’s my reasoning. So the fact that many nested scoped slots would require more boilerplate could actually decrease the use of anti-patterns.

  • The API surface area is drastically reduced, because most Vue developers will only have to remember $slot, which is just an object.

  • By using a $-prefixed property, we’re building on the web component slot API in a way that makes it clear that $slot is a Vue thing.

  • Currently, libraries often do something like <slot v-bind="user" /> so that their users can save a few characters with slot-scope="user" instead of slot-scope="{ user }. It seems elegant at first glance, but I’ve come to experience as an anti-pattern. The problem arises when the component wants to expose data other than the user. Then they have two choices: make a breaking change to their API or force this new property onto the user object, even though it may have very little to do with the user. Neither is a great option. Fortunately, $slot would remove the temptation to make components less future-proof, because while $slot is still shorter than $slot.user, you lose important context by aliasing the user as $slot.

Disadvantages

  • In some nested scoped slots, there are some edge cases where it won’t be obvious which component some data came from. In these cases though, users can still reach for slot-scope when they need maximum explicitness so I don’t think it’s a big deal. Plus, as I mentioned earlier, I think it’s very likely the user is shooting themselves in the foot with state provider components if they have this problem in the first place.
5reactions
Justineocommented, Dec 11, 2018

If I understand correctly, v-scope only serves one use case. Instead of writing:

<foo>
  <template slot-scope="{ item }">{{ item.id }}</template>
</foo>

We can write:

<foo v-scope="{ item }">{{ item.id }}</foo>

It does reduced quite a lot noise in this case but it seems to be the only case. It feels like some kind of overkill to introduce a new directive (which works quite differently from other directives, even from v-for because it works only on components and relies on <slot> logic underneath). Another concern is that when I search for v-scope in this repo, the only two occurrences are both discussing about reducing the data scope of a template subtree (https://github.com/vuejs/vue/issues/5269#issuecomment-288912328, https://github.com/vuejs/vue/issues/6913), like how with works in JavaScript.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Isaac Lyman on Twitter: "@chrisvfritz I've never been able to use ...
[Abandoned] RFC: Simplify scoped slot usage · Issue #9180 · vuejs/vue. This is a follow up of #7740 (comment) Rational Problems with current...
Read more >
Slots - Vue.js
In 2.6.0, we introduced a new unified syntax (the v-slot directive) for named and scoped slots. It replaces the slot and slot-scope attributes, ......
Read more >
What problems are Vue.js scoped slots trying to solve? - Morioh
Let's quickly cover slots and how we use them in Vue, a regular slot is simply a way of passing data into a...
Read more >
RFC 8264: PRECIS Framework: Preparation, Enforcement ...
Code Components extracted from this document must include Simplified BSD ... rules for strings placed in various protocol slots (such as addresses and ......
Read more >
RFC 7698 - Framework and Requirements for GMPLS-Based ...
In this scope, this document uses the term "flexi-grid enabled DWDM network" to refer to a network in which switching is based on...
Read more >

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found