Skip to content

perf(Button): render LinkBase directly for plain buttons#6738

Draft
benjamincanac wants to merge 1 commit into
perf/narrow-icon-propsfrom
perf/button-fast-path
Draft

perf(Button): render LinkBase directly for plain buttons#6738
benjamincanac wants to merge 1 commit into
perf/narrow-icon-propsfrom
perf/button-fast-path

Conversation

@benjamincanac

Copy link
Copy Markdown
Member

🔗 Linked issue

Relates to #4154, #6293

❓ Type of change

  • 👌 Enhancement (improving an existing functionality)

📚 Description

Every UButton renders a ULink, even when it isn't a link. In that case ULink resolves nothing: it computes slot props and wraps the content in a reka Slot, adding two component instances per button whose only job is prop plumbing. Layer profiling (#6736) showed that per-instance overhead (Vue prop diffing and reactivity bookkeeping) is where most of a plain button's render cost lives.

This renders ULinkBase directly when no link-shaping prop is set (to, href, target, rel, noRel, external, all read through the props proxy so theme defaults still apply). Link buttons take the exact same path as before, and ULink/ULinkBase remain untouched, so patterns like NavigationMenu's custom slot keep working as is.

Measured with the #6736 benches on this branch: plain Button re-render goes from 4,103 to 6,100 ops/s (+49%) and mount from 80 to 129 ops/s for a 25-button toolbar (+53%). In a playground reproduction with a 1,000-button grid the gain is visible in interaction timings. Output is byte-identical: full suite passes with zero snapshot changes, including attribute order.

Two things worth focused review:

  • Button now sets inheritAttrs: false and forwards $attrs explicitly in both branches, positioned to preserve the exact DOM attribute order fallthrough attrs had when they traveled through ULink's slot props (a first version reordered aria-label vs class and failed 703 snapshots, which would also have broken downstream users' snapshot tests).
  • The leading/label/trailing content block is duplicated across the two template branches: sharing it through a reusable template would reintroduce a component instance, which is exactly the overhead being removed.

Stacked on #6736.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

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.

1 participant