Template
wybthon.template¶
template
¶
Template-based mounting: static HTML serialization plus hole wiring.
This module is the runtime analogue of SolidJS's compiled templates. A
run-once component returns a VNode tree whose structure is static; only
reactive holes, event handlers, refs, and reactive prop bindings change
after mount. That means the static skeleton can be serialized to an HTML
string in Python (cheap) and registered with the rendering kernel once;
every mount of the same skeleton is a single CLONE_TPL op that clones
the pre-parsed tree natively. The kernel then walks the clone in
document order, assigning a dense block of node ids.
Because the Python serializer counts nodes in exactly the same pre-order
the kernel walks, every element, text node, and placeholder comment gets
a predictable id with zero extra communication: the mount simply
assigns first_id + k to the k-th serialized node.
Static text content is hoisted out of the HTML: the serializer emits
a one-space placeholder text node and records the real value as a
SET_TEXT binding applied after the clone. Hoisting is what makes
templates shared: a thousand list rows that differ only in their text
(ids, labels) produce the same skeleton string, so the browser parses
it once and clones it a thousand times.
The pipeline:
build_planwalks a VNode tree and produces aMountPlan: the serialized HTML, the pre-order node list (for id assignment and text bindings), the dynamic bindings (events, reactive props, refs, DOM-property writes), orNonewhen the tree isn't eligible for the fast path.- The reconciler registers the HTML (once per unique skeleton),
allocates the id block, emits the
CLONE_TPLop, applies bindings by id, and mounts dynamic children (holes, fragments, components) at their placeholder comments.
Trees fall back to per-node ops (still batched, still one bridge
crossing) when they contain constructs the HTML parser would mangle:
adjacent or empty text nodes, raw text elements, invalid attribute
names, or element nestings the parser rewrites (implied <tbody>,
auto-closed <p>, and similar).
Plans are cached per shape. The generic walk collects instance data while building a hashable shape key; serialization and HTML eligibility validation run only on the first mount of that shape. Repeated shapes also receive a guarded Python extraction routine, avoiding generic tree walking and prop classification on subsequent mounts. The routine checks structure and binding kinds and falls back when they change. Both caches are bounded, and instance handlers, refs, and expressions aren't retained.
Classes:
| Name | Description |
|---|---|
MountPlan |
Serialized mount plan for a static-skeleton VNode subtree. |
Functions:
| Name | Description |
|---|---|
build_plan |
Serialize |
MountPlan
¶
MountPlan(html: str, order: list[tuple[int, VNode, VNode | None]], bindings: list[tuple[VNode, int, str, Any]])
Serialized mount plan for a static-skeleton VNode subtree.
Attributes:
| Name | Type | Description |
|---|---|---|
html |
The serialized HTML string for the skeleton. Static text content is hoisted (each text node appears as a single-space placeholder), so structurally-identical trees share the same string regardless of their text. |
|
order |
Pre-order list of |
|
bindings |
List of |
build_plan
¶
Serialize vnode's static structure, or return None when ineligible.
Eligible trees have an element root and contain only element/text
nodes plus dynamic placeholders (holes, fragments, components). The
VNode tree is normalized in place (children lists become VNode
lists) as a side effect, exactly as the per-node mount path does.
A guarded extraction routine handles repeated shapes. If none matches, a generic walk collects instance bindings and a shape key. The HTML string and eligibility verdict come from the shape cache; the full serializer runs only on the first mount of each shape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vnode
|
VNode
|
An element VNode (string tag, not |
required |
Returns:
| Type | Description |
|---|---|
MountPlan | None
|
A |
What's in this module¶
template is the runtime analogue of SolidJS's compiled templates. A
run-once component returns a tree whose structure is static; only
holes, event handlers, refs, and reactive prop bindings change after
mount. So the static skeleton is serialized to an HTML string once,
registered with the kernel (REGISTER_TPL), and every later mount of
the same shape is a single CLONE_TPL op. The kernel walks the clone in
pre-order and assigns a dense block of node ids, which the Python side
predicts with no read-backs.
| Name | Description |
|---|---|
build_plan |
Serialize an element VNode's static structure, or return None when the tree must use per-node ops. |
MountPlan |
The result: html, the pre-order order list, the dynamic bindings, and node_count. |
Application code never calls these; the reconciler does.
How a plan is built¶
- Static tags, attributes, classes, styles, and datasets go into the HTML string directly.
- Static text is hoisted: it serializes as a one-space placeholder
and is applied as a
SET_TEXTbinding after the clone. This is what lets a thousand list rows that differ only in their text share one skeleton. - Reactive props are recorded as bindings and wrapped in per-prop render
effects after ids are assigned; event handlers become
LISTENops;value,checked, andinnerHTMLare applied as DOM properties. - Dynamic children (holes, fragments, components) serialize as comment placeholders and are mounted at that position afterwards.
- Plans are cached per shape: serialization and eligibility checks run on the first mount of a shape, and later structurally identical trees are a dictionary hit.
When the fast path is skipped¶
build_plan returns None, and the reconciler falls back to per-node
ops (still batched into the same commit), when the HTML parser wouldn't
reproduce the tree faithfully: fewer than three nodes, adjacent or empty
text nodes, raw-text elements such as <script> and <textarea>, SVG
and MathML subtrees (mounted with CREATE_ELEMENT_NS instead), invalid
attribute names, or nestings the parser rewrites (bare text inside
<table>, an implied <tbody>, an auto-closed <p>). The reconciler
also skips templates when the backend can't parse HTML (a stub document
without <template> support). The fallback is purely a performance
difference; behavior is identical.
See also¶
- Kernel: the
REGISTER_TPLandCLONE_TPLops - Reconciler: mounts plans and wires bindings
- Concepts: Virtual DOM
- Guides: Performance