-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Something I've been wondering about recently — whether we should replace {{yield}}
(an Ember idiom Ractive stole that we then copied) with <slot></slot>
, which is the 'platform' equivalent.
A quick primer on <slot>
In Svelte, you might do something like this:
<!-- App.html -->
<MyComponent>
hello!
</MyComponent>
<!-- MyComponent.html -->
some yielded content: {{yield}}
The web components equivalent would be this:
<template id='my-component'>
some slotted content: <slot></slot>
</template>
<script>
class MyComponent extends HTMLElement {
constructor() {
super();
const template = document.querySelector('#my-component').content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(template.cloneNode(true));
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component>
hello!
</my-component>
Web components also have named slots, and fallback content for slots, meaning you can do this:
<template id='my-component'>
<h1>my component</h1>
<slot>unnamed fallback</slot>
<h2>named slot foo</h2>
<slot name='foo'>foo fallback</slot>
<h2>named slot bar</h2>
<slot name='bar'>bar fallback</slot>
</template>
<script>/* gross setup code goes here */
<my-component>
some content for the unnamed slot
<span slot='foo'>the foo content</span>
</my-component>
This results in something like the following:
Pros and cons
There are several advantages to using <slot>
over {{yield}}
:
- Transferrable knowledge. If people already know web components, they have a headstart with Svelte component composition. Likewise, if they learn
<slot>
in a Svelte context, they will be able to hit the ground running with web components. - Alignment with The Platform™. While a lot of us have been somewhat sceptical about web components generally, there are situations where they make a lot of sense, and there's no point in doing things differently (other things being equal). More on this later
- Named slots are a nice feature. (Ractive has named
{{yield foo}}
blocks, which we haven't yet implemented) - Fallback content is another nice feature
- It suggests a public API — imagine doing
myComponent.slots.foo.appendChild(content)
. More on this later
There are some possible downsides as well:
- It's extra markup, if the
<slot>
is actually rendered to the DOM (rather than simply treated as a placeholder) - It means we're locked into the web components way of doing things
Personally I'm more persuaded by the pros than the cons. Are there things I'm missing?
Aligning with web components
We've discussed this previously (#12) without any real resolution — so far, the way to generate web components from Svelte components is to use svelte-custom-elements. There are arguments for generating web components directly from the compiler, aside from mere convenience. In particular, true style encapsulation (not just Svelte-style style leakage prevention, but protection from global styles) is a nice feature, and it's good for performance since the browser can isolate stuff more effectively.
From a DX perspective, compiling directly to custom elements is better than using svelte-custom-elements, because svelte-custom-elements puts everything in shadow DOM. Using light DOM (i.e. <slot>
) means you can see your component tree in devtools, for example.
I've talked in the past about how web components are sub-optimal when it comes to dataflow, because the DOM is a terrible API for that. But it occurs to me that Svelte-generated web components could have all the same methods regular Svelte components do, so you could (for example) do this:
document.querySelector('my-component').set({
foo: 1,
bar: 2
});
In other words, you can retain the benefits of synchronous, predictable updates, without sacrificing the performance benefits of batching.
Another more cynical reason to align with web components: the web components community. Svelte has an opportunity to be the favoured way of creating web components, since authoring them by hand involves writing some hilariously ugly code, and every other way creates a dependency on a framework. So there's a PR advantage that we shouldn't ignore.
(For the avoidance of doubt: none of this means that web components would be in any way required. It would still be possible to SSR a Svelte app without custom elements, for example. Best of all possible worlds!)
Public API
This issue was prompted in part by this Gitter convo with @m59peacemaker about having a public API for yielded content.
The suggestion there was that yield
could be a public initialisation option (as opposed to an internally used _yield
):
new Component({
target,
yield: [someDiv, someOtherDiv]
});
If we had <slot>
elements in the component template, they could be properties of the component, which would allow this sort of thing:
component = new Component({
target
});
component.slots.foo.appendChild(someDiv);
component.slots.foo.appendChild(someOtherDiv);
We could also have a combination of the two. We would also need a way of targeting an unnamed <slot>
— this is kind of ugly but maybe works?
component.slots[''].appendChild(someDiv);
component.slots[''].appendChild(someOtherDiv);
Thanks for reading this far, if anyone did. Let me know your thoughts!