bench: @choojs/html vs nanohtml v1 vs µhtml v5, happy-dom + real Chromium

npm run bench (happy-dom, all three engines) and npm run bench:browser
(Playwright Chromium, the two ESM engines). Honest numbers recorded in
docs/v8.md: we create ~12% faster than µhtml in real Chromium (the
parse-once/clone design), µhtml updates in place ~5x faster than
fresh-tree + nanomorph (choo's architectural cost, mitigated by component
caching), and server strings are on par with nanohtml v1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NgfSjHE11oFpoSnLVKLXd
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-08 17:59:53 +02:00
co-authored by Claude Fable 5
parent 63b56144a5
commit c9f5ba2fda
4 changed files with 214 additions and 5 deletions
+15 -3
View File
@@ -44,9 +44,21 @@ everything new lives under `packages/`.
wildcard decode, decoded state.href
- [x] Phase 2 (core): browser renderer rewrite, `@choojs/component`,
zero-build counter example, full-app integration test in happy-dom
- [ ] Phase 2 (remaining): adoption-style hydration with mismatch
warnings, Playwright suite in a real browser, benchmarks vs
nanohtml v1 / µhtml
- [x] Phase 2 (tail): adoption-style hydration with mismatch warnings
(`@choojs/html/hydrate`, wired into `mount()`), real-browser
Playwright e2e (zero-build page, SSR-then-hydrate page, adoption
proof; CI job included), benchmarks vs nanohtml v1 / µhtml
## Benchmarks (2026-09, 100-row table, `npm run bench` / `bench:browser`)
Real Chromium: @choojs/html creates fresh trees ~12% faster than µhtml v5
(3.5k vs 3.1k ops/s) — the parse-once/clone design pays off in native DOM.
µhtml updates in place ~5x faster than our fresh-tree + nanomorph loop
(3.0k vs 0.6k ops/s): that is choo's architectural cost, mitigated in real
apps by @choojs/component caching (proxy nodes skip unchanged subtrees),
and the number to beat if Phase 3 explores keyed-hole optimizations.
Server string rendering is on par with nanohtml v1 (~13k ops/s, within
6%). happy-dom numbers in bench/render.js are indicative only.
- [ ] Phase 3: v8 wiring (`toStream`, devtools, codemod). Includes the
answer to choojs/choo#653 (async/lazy routes): route handlers may
resolve lazily via native import(); browser renders previous tree