Archived
143137e2b4
Counter.el defines Counter, CounterPair, and App components with {#if}
conditionals, event handlers, and child component composition.
render.js (generated by elc-ui --target=server) is the Node.js render
script the server calls. app.js (generated by elc-ui --target=web) is the
compiled browser module. hydrate.js attaches the runtime to the
server-rendered DOM without replacing it.
index.html shows the integration point with instructions for injecting
server output and the two CLI commands needed to regenerate both targets.
50 lines
2.1 KiB
JavaScript
50 lines
2.1 KiB
JavaScript
/**
|
|
* hydrate.js — Client-side hydration entry point for ssr-counter.
|
|
*
|
|
* This script runs in the browser after the server has delivered the
|
|
* SSR-rendered HTML. It:
|
|
* 1. Finds each server-rendered component root (data-el-component attribute).
|
|
* 2. Reads back the initial state snapshot (data-el-state attribute).
|
|
* 3. Attaches the el-ui runtime WITHOUT replacing the existing DOM.
|
|
* 4. Binds all event handlers so the page becomes interactive.
|
|
*
|
|
* The page is immediately visible (the SSR HTML), and becomes interactive
|
|
* as soon as this script finishes executing. There is no content flash.
|
|
*
|
|
* SSR + hydration lifecycle:
|
|
* Server: renderToString(src, 'App', props) → HTML with markers
|
|
* Network: HTML delivered to browser, painted immediately
|
|
* Client: hydrate('[data-el-component="App"]', App) → events bound
|
|
* spreading activation activates on first setState() call
|
|
*/
|
|
|
|
import { hydrate } from '../../dist/el-ui.js';
|
|
import { Counter, CounterPair, App } from './app.js';
|
|
|
|
// Hydrate each component by its data-el-component selector.
|
|
// Order matters: hydrate leaf components before their parents so
|
|
// each instance gets its own renderer and state graph.
|
|
|
|
// Hydrate all Counter instances
|
|
document.querySelectorAll('[data-el-component="Counter"]').forEach(el => {
|
|
// Read the stable ID to avoid double-hydration
|
|
if (el.dataset.elHydrated) return;
|
|
hydrate(`[data-el-id="${el.dataset.elId}"]`, Counter);
|
|
el.dataset.elHydrated = 'true';
|
|
});
|
|
|
|
// Hydrate all CounterPair instances
|
|
document.querySelectorAll('[data-el-component="CounterPair"]').forEach(el => {
|
|
if (el.dataset.elHydrated) return;
|
|
hydrate(`[data-el-id="${el.dataset.elId}"]`, CounterPair);
|
|
el.dataset.elHydrated = 'true';
|
|
});
|
|
|
|
// The App component contains CounterPair and Counter children.
|
|
// If you want the App to own the full activation graph, hydrate it here.
|
|
// For this example we hydrate individual Counter components so each
|
|
// maintains its own independent state graph (the simpler model).
|
|
|
|
// Uncomment to hydrate the whole App as one unit instead:
|
|
// hydrate('[data-el-component="App"]', App);
|