Repository navigation
Conversation
Data class/style values stay in the tracked layer effects (a store object can change in place), data keys apply inside an effect so hydration owns the writes, dev tagElement runs, and form elements or views with namespaced or child-content keys keep the single attribute effect.
🦋 Changeset detectedLatest commit: 5424730 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will regress 2 benchmarks1 warning
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
meh not sure its worth it. Targets one very specific benchmark |
|
Thanks for this, @brenelz. Halving the update time on the polymorphic chain is a real win. Ryan read the review and wants a rework rather than landing it as is. Both of your open questions are the reasons:
Where the time actually goes, per your own write-up: the two compiled
That way the update win stays without paying in mount time, effect count or bytes. Generated by Claude Code |
Draft to see the CodSpeed run; two design questions below are open.
What was slow
On the Kobalte-shaped polymorphic chain (
test/harness/polymorphic.tsx), updating one row label took twice as long as the same element written as plain JSX. The DOM writes are identical in both forms (1600 each ofaria-label,titleand text per pass), so the gap was all JavaScript.spread()puts every prop of an element in one attribute effect. A label change reran all 13 getters through the merge/omit layers, including two compiledmemo(() => !!isNativeButton())conditions that are rebuilt on every run, andassign()then wrote only the two attributes that changed.What changed
When the spread source is a merge/omit view over plain objects (
hasStaticKeys),spread()now:class/stylealways go to the tracked layer effects, because a data store object can change in place. Form elements (DOMWithState) and views with aref, a namespaced key (prop:,attr:,on:) or a child-content key (innerHTML,textContent,innerText) keep the single effect, whose application order they depend on.Measurements
The numbers come from a standalone harness on production builds: vite compiles the polymorphic harness, and Node with jsdom runs it with
--conditions=browser. Each figure is the minimum over many batches. Vitest roughly doubles production times here (its module transform), so I didn't use it for these.The other spread benches (
spread-enumerate,style-class-object,dynamic-tag) moved between -8% and +1%.Open questions
spread()creates three reactive nodes per element #3388 cap. A spread could create at most two reactive nodes. A view spread now creates one effect per getter-bearing layer, plus one for its data properties.Verification
npx vitest run,npx vitest run --config vite.config.hydrate.mjsandnpx vitest run --config vite.config.server.mjsinpackages/weball pass (1626, 716 and 1911 tests).test/spread-nodes.spec.tsx: a change reruns only its own layer's getters (fails onnext); a dataclassstore stays tracked;classbesideprop:classNamekeeps the single effect's winner.🤖 Generated with Claude Code