Open dgp1130 opened 11 months ago
Thank you so much @dgp1130 !
Such a thorough explanation with examples will surely get me started with this. There are some parts that will be reworked a bit under the surface and with those I'll keep this in mind.
I'll come back to you if I find something I need more examples on. Thanks!
Note that this is a Lit-only feature. If you're going to do this I would make sure that it's a Lit project and not a generic WC one.
Why is this Lit only? defet-hydration
is a proposed community protocol. Any components/frameworks which implement that protocol should be compatible with this feature.
https://github.com/webcomponents-cg/community-protocols/pull/15
It would be very cool for WCDT to visualize which components on the page were hydrated and which ones were not, perhaps by putting colored borders around custom elements in the page, with different colors for different hydration states. This would help developers understand when and where their page is hydrating and give better insights into page performance. It can also help devs fix bugs caused by components not hydrating, since it would help easily identify which components failed to hydrate as expect. An extra feature could be denoting (whether in color or another means) why a component has not hydrated. Is it because the component is not defined, or because hydration was deferred?
This is most useful for pages using an "islands" architecture, where there are multiple components which can independently hydrate based on their own defined conditions.
Unfortunately there is no browser spec for what "hydration" means or how to identify hydrated and dehydrated components. However, there is a proposed community protocol which specifies
defer-hydration
as an attribute which is used to prevent hydration on specific components. I think the right behavior here is to defined "hydrated" elements as those which are 1) defined and 2) are missingdefer-hydration
. By contrast, "dehydrated" elements are either 1) not defined or 2) havedefer-hydration
set as an attribute.Based on this, the feature can likely be implemented by injecting two styles into the page:
Which color should indicate which state is a UX choice I don't feel that strongly about. I'm personally of the opinion that every hydrated element has a negative cost to the page and we want to defer or avoid hydrating as many components as possible for as long as possible, and therefore hydrated components should be red to signify that. I get that others will likely disagree with that assessment. However I also believe code diffs should have added code in red and removed code in green, so clearly I'm insane.
Unfortunately this CSS is a bit oversimplified because:
:not(:defined)
, meaning they will all be highlighted green, which would be far too noisy. To my knowledge, there is no selector which restricts itself to custom elements such that you could highlightmy-component
but not highlightdiv
. There are some potential workarounds, but they are not pretty.defer-hydration
removed and hydrates itself, then hasdefer-hydration
added back, the styles will show the component as being dehydrated, even though it actually did hydrate. Addingdefer-hydration
does not "unhydrate" an element, so technically this is a stateful change. This can probably be overlooked for an MVP implementation, but it would be great if there's a way to handle that.An alternative approach might be to track this with JavaScript. There are three ways the page can observe a component's hydration state change:
MutationObserver
withsubtree: true
.defer-hydration
is not set. We can observe this withcustomElements.whenDefined
.defer-hydration
removed while it is defined. We can observe this with aMutationObserver
withattributes: true
.We can watch for new elements and state changes to automatically update their styles.
I haven't tested this and it isn't a perfect script, but hopefully good enough. I'm not sure how
MutationObserver
plays with shadow DOM, but if it doesn't pierce that boundary then you'll still have that problem. The CSS for.wcdt-dehydrated
and.wcdt-hydrated
also needs to be applied to all shadow roots, which can be tricky. Maybe a constructable style sheet which gets added to all shadow roots? Not sure if an attached shadow root can be observed (maybe patchElement.prototype.attachShadow
?). This approach at least avoids the custom element selector problem. You'll also need to either inject this script before HTML is parsed or scan the document immediately on load for components which are appended prior to the.observe()
call.I made a Stackblitz demoing all the cases I could think of. This should serve as a good test for the feature to make sure it paints the borders correctly.
https://stackblitz.com/edit/typescript-yryatj?file=index.ts,index.html