Skip to content

Conversation

@kfule
Copy link
Contributor

@kfule kfule commented Nov 7, 2025

Description

This PR fully separates the normalization of class and className from the merging of vnode.attrs and vnode.state.attrs, which were previously handled together in the same code path. This simplifies conditional branches and improves bundle size and performance.

results of `npm run perf` on my laptop (about 3-4% improvements for `rerender same tree`)

results of this PR (after)

construct large vnode tree x 29,108 ops/sec ±0.34% (127 runs sampled)
rerender identical vnode x 5,181,213 ops/sec ±0.98% (130 runs sampled)
rerender same tree x 107,123 ops/sec ±0.15% (126 runs sampled)
rerender same tree (with selector-generated attrs) x 140,165 ops/sec ±0.18% (125 runs sampled)
add large nested tree x 9,171 ops/sec ±1.73% (123 runs sampled)
mutate styles/properties x 140 ops/sec ±1.14% (98 runs sampled)
repeated add/removal x 3,061 ops/sec ±1.06% (122 runs sampled)

results of #3052 (before)

construct large vnode tree x 27,820 ops/sec ±0.47% (129 runs sampled)
rerender identical vnode x 5,068,693 ops/sec ±0.96% (129 runs sampled)
rerender same tree x 103,402 ops/sec ±0.17% (126 runs sampled)
rerender same tree (with selector-generated attrs) x 134,861 ops/sec ±0.19% (127 runs sampled)
add large nested tree x 8,972 ops/sec ±1.50% (124 runs sampled)
mutate styles/properties x 145 ops/sec ±1.37% (99 runs sampled)
repeated add/removal x 3,013 ops/sec ±1.52% (122 runs sampled)

Note that "rerender same tree (with selector-generated attrs)" is measured by changing the code as in #3041.

Motivation and Context

I’ve iterated on this section a few times already (b17819e, e6ee5d0 (cc0df94)), and this PR is intended to be the final refinement of that work.

How Has This Been Tested?

  • npm run test
    • It passes all existing tests as-is, including detailed test cases for hyperscript.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My change requires a documentation update, and I've opened a pull request to update it already:
  • I have read https://mithril.js.org/contributing.html.

This commit fully separates the normalization of `class` and `className` from the merging of `vnode.attrs` and `vnode.state.attrs`, which were previously handled together in the same code path.
This simplifies conditional branches and improves bundle size and performance.
@kfule kfule requested a review from a team as a code owner November 7, 2025 12:54
@kfule
Copy link
Contributor Author

kfule commented Nov 8, 2025

I found that this change causes attrs.class passed as a parameter to “always” become null if hasOwn.call(attrs, "class") is true. This means that even in cases where it merges with static attributes to create a separate object, the passed attrs.class will be null.

However, this behavior where the passed attrs.class becomes null (or undefined in v1) has existed since v1, and I believe the impact is minimal.

@dead-claudia
Copy link
Member

For future reference, can you post benchmarks before the change as well? It'd help put numbers into context a bit more.

@dead-claudia dead-claudia merged commit 170e8dc into MithrilJS:main Nov 8, 2025
7 checks passed
@JAForbes JAForbes mentioned this pull request Nov 8, 2025
@kfule
Copy link
Contributor Author

kfule commented Nov 8, 2025

Thank you for merging!
I've updated the description to make the benchmark results comparison clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants