[lit-ssr] Add defer-hydration protocol for coordinating host/child hydration#1388
[lit-ssr] Add defer-hydration protocol for coordinating host/child hydration#1388kevinpschaaf merged 10 commits intolit-nextfrom
Conversation
📊 Tachometer Benchmark ResultsSummarynop-update
render
update
update-reflect
Resultslit-element-list
render
update
update-reflect
lit-html-kitchen-sink
render
update
nop-update
lit-html-repeat
render
update
lit-html-template-heavy
render
update
reactive-element-list
render
update
update-reflect
updating-element-list
render
update
update-reflect
|
deaa64f to
63ea0c2
Compare
7c862ad to
1dc02df
Compare
| * # template | ||
| * <div class="TEST_X"> | ||
| * <!--lit-bindings 0--> # Indicates there are attribute bindings here | ||
| * <!--lit-node 0--> # Indicates there are attribute bindings here |
There was a problem hiding this comment.
Let's rename this file experimental-hydrate.js
sorvell
left a comment
There was a problem hiding this comment.
Basically seems ok, but let's rename the hydrate file to experimental-hydrate.
| ) { | ||
| if (this.shadowRoot) { | ||
| this._$needsHydration = true; | ||
| } |
There was a problem hiding this comment.
Why does _$needsHydration need to be set here?
| // call the base implementation, which would also adopt styles (for now) | ||
| const createRenderRoot = LitElement.prototype.createRenderRoot; | ||
| LitElement.prototype.createRenderRoot = function (this: PatchableLitElement) { | ||
| if (this._$needsHydration) { |
There was a problem hiding this comment.
Should set _$needsHydration here.
050c553 to
95b022b
Compare
| if (name === 'defer-hydration' && value === null) { | ||
| connectedCallback.call(this); | ||
| } else { | ||
| attributeChangedCallback.call(this, name, old, value); |
There was a problem hiding this comment.
I think you should always call into the original attributeChangedCallback. What if an element wanted to do something special on hydration?
There was a problem hiding this comment.
Yeah good call. Updated.
| // super.connectedCallback() | ||
| const attributeChangedCallback = | ||
| LitElement.prototype.attributeChangedCallback; | ||
| LitElement.prototype.attributeChangedCallback = function ( |
There was a problem hiding this comment.
I think it might be good to put as much of this logic onto ReactiveElement as possible for other subclasses to use...
There was a problem hiding this comment.
Let's do that in a later PR, since it involves adding a hydrate-support for reactive-element, which doesn't yet exist.
Goal: Ensure an SSR'ed LitElement does not enable and hydrate itself before its initial data has been supplied to it from its host (by virtue of its host being hydrated). Since in SSR, element upgrade occurs as a result of CE registration being loaded, and since module loading order naturally occurs leaves-up (by virtue of parents depending on their children), a child will typically run its
connectedCallback(which in turn schedules its update) before its host when the application bundle is loaded.Approach:
<!--lit-node-->marker to denote each custom element. Note, the previous<!--lit-bindings-->marker was repurposed (and renamed) to now indicate any node that either has attribute bindings or is a custom element.custom-element-attributesopcode, if the custom element host stack is >1 deep (meaning the CE is within another CE), then adefer-hydrationattribute is emitted on the CE. A newcustomElementHostStack(distinct fromcustomElementInstanceStack) is introduced to track shadow-dom hosts only (the instance stack tracks every open CE, including light-dom parents)<!--lit-node-->is encountered, in addition to looking for bound attributes, we also look for thedefer-hydrationattribute after hydrating attribute/property/event/element parts, and if present we simply remove ithydration-supportmodule patchesobservedAttributes,attributeChangedCallback, andconnectedCallback, such thatconnectedCallbackdoes not run theLitElement.prototype.connectedCallbackif it has adefer-hydrationattribute at connected time; in that case,attributeChangedCallbackdetects whendefer-hydrationis removed and only then calls the baseconnectedCallback, causing the element to enable updating and ultimately hydrate itself.This PR also fixes a latent bug with hydrating attribute parts on void elements: we need to check previousSibling of the
<!--lit-node>marker for void elements first, and then use parentElement otherwise (it's guaranteed to be the first node of a parent otherwise).