Isomorphic hydration: skip re-render when server HTML present
sx-mount now checks if the target element has children (server- rendered HTML). If so, skips the client re-render and only runs hydration (process-elements, hydrate-islands, hydrate-elements). This preserves server-rendered CSSX styling and avoids the flash of unstyled content that occurred when the client replaced the server HTML before re-rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||||
var SX_VERSION = "2026-03-23T15:39:32Z";
|
var SX_VERSION = "2026-03-23T16:18:55Z";
|
||||||
|
|
||||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||||
@@ -4627,16 +4627,12 @@ PRIMITIVES["hoist-head-elements-full"] = hoistHeadElementsFull;
|
|||||||
// sx-mount
|
// sx-mount
|
||||||
var sxMount = function(target, source, extraEnv) { return (function() {
|
var sxMount = function(target, source, extraEnv) { return (function() {
|
||||||
var el = resolveMountTarget(target);
|
var el = resolveMountTarget(target);
|
||||||
return (isSxTruthy(el) ? (function() {
|
return (isSxTruthy(el) ? ((isSxTruthy(isEmpty(domChildList(el))) ? (function() {
|
||||||
var node = sxRenderWithEnv(source, extraEnv);
|
var node = sxRenderWithEnv(source, extraEnv);
|
||||||
domSetTextContent(el, "");
|
domSetTextContent(el, "");
|
||||||
domAppend(el, node);
|
domAppend(el, node);
|
||||||
hoistHeadElementsFull(el);
|
return hoistHeadElementsFull(el);
|
||||||
processElements(el);
|
})() : NIL), processElements(el), sxHydrateElements(el), sxHydrateIslands(el), runPostRenderHooks()) : NIL);
|
||||||
sxHydrateElements(el);
|
|
||||||
sxHydrateIslands(el);
|
|
||||||
return runPostRenderHooks();
|
|
||||||
})() : NIL);
|
|
||||||
})(); };
|
})(); };
|
||||||
PRIMITIVES["sx-mount"] = sxMount;
|
PRIMITIVES["sx-mount"] = sxMount;
|
||||||
|
|
||||||
|
|||||||
23
web/boot.sx
23
web/boot.sx
@@ -79,16 +79,19 @@
|
|||||||
;; extra-env: optional extra bindings dict
|
;; extra-env: optional extra bindings dict
|
||||||
(let ((el (resolve-mount-target target)))
|
(let ((el (resolve-mount-target target)))
|
||||||
(when el
|
(when el
|
||||||
(let ((node (sx-render-with-env source extra-env)))
|
;; If the server already rendered content (isomorphic SSR),
|
||||||
(dom-set-text-content el "")
|
;; skip re-render — just hydrate the existing DOM.
|
||||||
(dom-append el node)
|
(when (empty? (dom-child-list el))
|
||||||
;; Hoist head elements from rendered content
|
(let ((node (sx-render-with-env source extra-env)))
|
||||||
(hoist-head-elements-full el)
|
(dom-set-text-content el "")
|
||||||
;; Process sx- attributes, hydrate data-sx and islands
|
(dom-append el node)
|
||||||
(process-elements el)
|
;; Hoist head elements from rendered content
|
||||||
(sx-hydrate-elements el)
|
(hoist-head-elements-full el)))
|
||||||
(sx-hydrate-islands el)
|
;; Process sx- attributes, hydrate data-sx and islands
|
||||||
(run-post-render-hooks))))))
|
(process-elements el)
|
||||||
|
(sx-hydrate-elements el)
|
||||||
|
(sx-hydrate-islands el)
|
||||||
|
(run-post-render-hooks)))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user