-
-
Save justinfagnani/936791248120749ff1f8188f1f4064d9 to your computer and use it in GitHub Desktop.
<!doctype html> | |
<html> | |
<head> | |
<script src="shadow-root.js"></script> | |
</head> | |
<div> | |
<div slot="main"> | |
I'm some projected content. | |
</div> | |
<shadow-root> | |
<template> | |
I'm some template content in a shadow-root. | |
<slot name="main"></slot> | |
<div> | |
<div> | |
Nested project content | |
</div> | |
<shadow-root> | |
<template> | |
<slot></slot> | |
</template> | |
</shadow-root> | |
</div> | |
</template> | |
</shadow-root> | |
</div> | |
</html> |
class ShadowRootElement extends HTMLElement { | |
connectedCallback() { | |
const template = this.children[0]; | |
if (template && template.tagName === 'TEMPLATE') { | |
const parent = this.parentElement; | |
let shadow = parent.shadowRoot; | |
if (!shadow) { | |
shadow = parent.attachShadow({mode: 'open'}); | |
} | |
const content = document.importNode(template.content, true); | |
shadow.appendChild(content); | |
} | |
} | |
} | |
customElements.define('shadow-root', ShadowRootElement); |
The problem with trying to send over the distributed tree is that you lose information, specifically the light DOM of elements, that you'd have to reconstruct somehow. This includes undistributed nodes, ordering of nodes distributed to different slots. On top of that you have to implement style scoping.
What I'm trying to show here with <shadow-root>
is polyfill a frequent request for declarative shadow DOM. The JS that executes here to build the tree of trees is much, much simpler than any real framework + element definitions would be, and the JS payload is very small. Of course there'd have to be some measurement, but I bet this type of tree is constructed extremely efficiently.
I think where this idea would go from here is:
- Add a
template
attribute to<shadow-root>
so that multiple shadow roots can share the same template. It would reference a template by id. - Have
<shadow-root>
remove itself when it stamps. - Build a serializer that takes a complex Shadow DOM based page, like YouTube, and outputs a page using
<shadow-root>
, de-duplicating shadow root contents to use the same template instance. - Measure payload size and rendering time of the complex page.
The problem with trying to send over the distributed tree is that you lose information, specifically the light DOM of elements, that you'd have to reconstruct somehow. This includes undistributed nodes, ordering of nodes distributed to different slots.
All very good points. Where I was trying to take it is be able to render a tree that looks the same before the shadow root is stamped out so that the initialisation work can be done separately. From what I can gather this is the biggest argument for SSR: perceived performance. Overall performance and TTI don't seem to be affected much by it, but it's an argument that comes up a lot. Whether or not it's valid in the grand scheme of things is somewhat irrelevant to the fact that the industry is flocking to it.
On top of that you have to implement style scoping.
If styled-jsx is used (not a general solution, though), it would be able to scope styles prior to stamping out and distribution.
I like your solution here for declarative shadow roots, but I don't see how it can help the problem I'm trying to solve. Though, the more I try and solve it, I don't think it's solvable with shadow DOM.
@justinfagnani I was just doing some work on the serialisation aspect of this and mini-filling shadow DOM (to work with Undom) on the server and was wondering if you knew if crawlers will index content. I know querySelector
calls won't go into them.
As a result of rendering, I'd much rather have something closer to what the composed tree would look like. For example:
In this example, the
<shadow-root />
element is similar to yours but in this case it's simply replaced by ashadowRoot
. Thedisplay: contents
style is added to not mess up styling. The<slot />
elements contain nodes that have aslotted
attribute which indicate they should be moved to light DOM, thus distributed. Any content in slots that don't have that attribute are left as default content.The idea behind this is so it looks like it would if it were hydrated without executing any JS. If you have to execute JS sync, I think that would negate some of the [arguable] benefits of server rendering. One may as well just stamp out the custom element with light DOM. Making this look good before hydration would be where
zeit/styled-jsx
comes in. After it doesn't really matter, but it works in both cases.Terms
hydrated
initialised with a shadow rootDISCLAIMER
This isn't a solid proposal and I'm still fleshing it out in my head.