What the compiler emits
Read the compiler's output for a component once; it explains most of the numbers.
Read the compiler's output for a component once; it explains most of the numbers. compile() from
@shadow-js/compiler is the same function the Vite plugin calls.
peek.tstsx
import { compile } from "@shadow-js/compiler";
const source = `
import { useStore } from "@shadow-js/core";
export function Row(props) {
const [count, setCount] = useStore(0);
const double = count() * 2;
return (
<li class="row" data-id={props.id}>
<b>static</b> {props.label} {count()} {double}
<button onClick={() => setCount(count() + 1)}>go</button>
</li>
);
}`;
const { code } = compile(source, { filename: "row.tsx", generate: "dom", dev: false, hmr: false });
const count = (pattern: RegExp): number => code.match(pattern)?.length ?? 0;
console.log(count(/_\$template\("/g)); // static markup, parsed once
console.log(count(/_\$effect\(/g)); // the data-id attribute
console.log(count(/_\$insert\(/g)); // three dynamic text slots
console.log(count(/_\$derived\(/g)); // `double`
console.log(JSON.stringify(["template", "effect", "insert", "derived"]));
1
1
3
1
[What the output shows for that component:
- Static markup is hoisted into one
template("<li class=\"row\">..."). The string is parsed once, on first use, and every row is acloneNodeof it.class="row"and<b>static</b>cost nothing per update. - Each dynamic child is its own
insertagainst its own anchor, so{props.label},{count()}and{double}update independently. A signal accessor passed as a child (count) is handed over as the accessor itself; an expression (props.label) is wrapped in an arrow function. - Each dynamic attribute is its own effect. Static attributes are in the template.
- Event handlers are delegated for the common events (
click,input,keydown, ...): the handler is stored on the element as$$click, and one listener per event name lives ondocument. A list of 10 000 buttons has one click listener. const double = count() * 2becomes a memo (derived), so it recomputes only whencountchanges and is shared by every place that reads it.
Pass /*@static*/ before an expression to tell the compiler it never changes, and autoReactive: false
to the Vite plugin to turn the derived-value rewrite off for a file (the
forgiving reactivity contract lists the rules).