Repository navigation
Lume.js v2.4.0 — 2026-07-20
Three performance fixes land: a kernel fast-path that skips flush work entirely for stores nobody observes, effect() re-runs that reuse subscriptions instead of rebuilding them, and repeat() reorders that move only the DOM nodes actually out of place.
Highlights
repeat()list reorders now cost O(k) DOM moves for a k-item change instead of cascading into every node after the first mismatch — a 167-swap storm across 2,000 tiles dropped from ~2,000 to ~310 moves per tick.effect()re-runs that read the same dependencies as their previous run now make zero subscribe/unsubscribe calls — a 3,000-store effect re-ran 7.5x faster (23.8ms → 3.2ms).- Writes to a store with no subscribers and no
$beforeFlushhooks skip the flush pipeline entirely — worst write-pass frame on a 3,000-cell stress grid dropped 156ms → 16ms (~10x) under a 20x CPU throttle.
New Features
- N/A — no new public APIs in this release.
Bug Fixes
- N/A — no bug fixes in this release; see Performance below for behavioral changes tied to the perf work.
Performance
- Kernel no-subscriber fast-path: the
settrap now skips pending-notification bookkeeping, batch enqueue, and microtask flush when a store has no subscribers on any key and no$beforeFlushhooks. High-churn stores read back by polling (e.g. a render loop) get reactive writes within ~1–2x of plain-object writes. Kernel cost: +0.03 KB gz (1.46 → 1.49). One subtle sharpening: a subscriber registered between a write and its microtask no longer receives a duplicate delivery of that write on flush — the immediate$subscribecall already handed it the current value. effect()subscription reuse: auto-tracked dependencies persist across runs in a per-effect registry instead of tearing down and rebuilding every run.computed()inherits the gain since it's built oneffect(). Tracking semantics unchanged; a run that throws now keeps its subscriptions instead of leaking listeners.repeat()minimal-move reconciliation: removes departed rows first, then keeps the longest stable chain of survivors (LIS over previous positions) and re-inserts only the rest. Full permutations are unchanged (nearly everything must move regardless); sparse reorders (swaps, drag-drop, sort-by-column) are the case this fixes. Same final order, lifecycle callbacks, and focus/scroll preservation; rows hosting iframes, videos, or running transitions no longer reset when other rows move past them.
Full measurement protocol and numbers for all three: see the 2.4.0 CHANGELOG entry.
Breaking Changes
| Change | Migration |
|---|---|
| — | — |
Deprecations
- N/A — nothing deprecated in this release.
Tests
475 tests passing, 100% statement/branch/function/line coverage maintained.
Bundle Sizes
| Entry point | Gzipped | Budget |
|---|---|---|
dist/state.mjs (universal core) |
1.49 KB | 1.75 KB |
dist/index.mjs (core + DOM) |
2.77 KB | 3.00 KB |
dist/addons.mjs |
4.96 KB | 6.00 KB |
dist/handlers.mjs |
1.23 KB | 2.00 KB |
dist/lume.global.js (CDN) |
7.75 KB | 8.00 KB |
Install
npm install lume-js@2.4.0CDN:
<script type="module">
import { state, bindDom, effect } from 'https://cdn.jsdelivr.net/npm/lume-js@2.4.0/dist/index.mjs';
</script>Global (no-module CDN):
<script src="https://cdn.jsdelivr.net/npm/lume-js@2.4.0/dist/lume.global.js"></script>Full Changelog
Compare: v2.3.1...v2.4.0