perf(runtime-dom): optimize array event handler dispatch#14828
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughEvent dispatch logic is inlined within ChangesEvent Handler Array Processing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@vue/compiler-core
@vue/compiler-dom
@vue/compiler-sfc
@vue/compiler-ssr
@vue/reactivity
@vue/runtime-core
@vue/runtime-dom
@vue/server-renderer
@vue/shared
vue
@vue/compat
commit: |
Size ReportBundles
Usages
|
bf46e08 to
cc5d253
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc5d253fa5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/runtime-dom/src/modules/events.ts (1)
122-133: ⚡ Quick winAvoid per-handler args-array allocations in the hot loop.
This path still allocates
[e]for every handler invocation. Hoist it once per dispatch to keep the allocation reduction goal intact.♻️ Proposed fix
if (isArray(value)) { const originalStop = e.stopImmediatePropagation e.stopImmediatePropagation = () => { originalStop.call(e) ;(e as any)._stopped = true } + const args = [e] for (let i = 0; i < value.length; i++) { if ((e as any)._stopped) { break } const handler = value[i] if (handler) { callWithAsyncErrorHandling( handler, instance, ErrorCodes.NATIVE_EVENT_HANDLER, - [e], + args, ) } } } else {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/runtime-dom/src/modules/events.ts` around lines 122 - 133, The hot-loop currently allocates a new args array for every handler call by passing [e] inline; hoist that allocation out of the loop by creating a single const args = [e] immediately before the for loop and pass args into callWithAsyncErrorHandling instead. Update the loop in events.ts where value (the handlers array) is iterated and ensure callWithAsyncErrorHandling(handler, instance, ErrorCodes.NATIVE_EVENT_HANDLER, args) is used so no per-handler array allocations occur.packages/runtime-dom/__benchmarks__/events.bench.ts (1)
5-22: ⚡ Quick winBenchmark body currently mixes setup cost with dispatch cost.
Given the bench names, consider moving
createElement+patchPropsetup outside the timed callback so the metric isolates dispatch performance.📊 Proposed refactor
describe('runtime-dom events', () => { + const singleEl = document.createElement('button') + let singleCount = 0 + patchProp(singleEl, 'onClick', null, () => singleCount++) + + const multiEl = document.createElement('button') + let multiCount = 0 + patchProp(multiEl, 'onClick', null, [ + () => multiCount++, + () => multiCount++, + () => multiCount++, + () => multiCount++, + ]) + bench('dispatch click with single handler', () => { - const el = document.createElement('button') - let count = 0 - patchProp(el, 'onClick', null, () => count++) - el.dispatchEvent(new Event('click')) + singleEl.dispatchEvent(new Event('click')) }) bench('dispatch click with multiple handlers', () => { - const el = document.createElement('button') - let count = 0 - patchProp(el, 'onClick', null, [ - () => count++, - () => count++, - () => count++, - () => count++, - ]) - el.dispatchEvent(new Event('click')) + multiEl.dispatchEvent(new Event('click')) }) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/runtime-dom/__benchmarks__/events.bench.ts` around lines 5 - 22, The benchmark callbacks currently include setup (document.createElement + patchProp) which contaminates dispatch timing; extract the setup for both benches so only el.dispatchEvent(new Event('click')) runs inside each bench callback: create the button element once per bench, call patchProp(el, 'onClick', null, ...) beforehand (for the single-handler and the array-of-handlers cases), then have the bench callback simply call el.dispatchEvent(...). Reference the existing bench(...) invocations, document.createElement, patchProp, and el.dispatchEvent when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/runtime-dom/__benchmarks__/events.bench.ts`:
- Around line 5-22: The benchmark callbacks currently include setup
(document.createElement + patchProp) which contaminates dispatch timing; extract
the setup for both benches so only el.dispatchEvent(new Event('click')) runs
inside each bench callback: create the button element once per bench, call
patchProp(el, 'onClick', null, ...) beforehand (for the single-handler and the
array-of-handlers cases), then have the bench callback simply call
el.dispatchEvent(...). Reference the existing bench(...) invocations,
document.createElement, patchProp, and el.dispatchEvent when making the change.
In `@packages/runtime-dom/src/modules/events.ts`:
- Around line 122-133: The hot-loop currently allocates a new args array for
every handler call by passing [e] inline; hoist that allocation out of the loop
by creating a single const args = [e] immediately before the for loop and pass
args into callWithAsyncErrorHandling instead. Update the loop in events.ts where
value (the handlers array) is iterated and ensure
callWithAsyncErrorHandling(handler, instance, ErrorCodes.NATIVE_EVENT_HANDLER,
args) is used so no per-handler array allocations occur.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d77a28ed-4eb2-4916-88f5-8fc6d8e8aeca
📒 Files selected for processing (3)
packages/runtime-dom/__benchmarks__/events.bench.tspackages/runtime-dom/__tests__/patchProps.spec.tspackages/runtime-dom/src/modules/events.ts
|
/ecosystem-ci run |
|
📝 Ran ecosystem CI: Open
|
Summary
Array.mapand wrapper closure allocations for runtime-dom event handler arrays.stopImmediatePropagation()behavior by stopping later handlers in the same array.Test Plan
test / unit-testtest / unit-test-windowstest / lint-and-test-dtstest / e2e-testgit apply --checkwas run against the fetched upstream target files before creating this branch.Suggested benchmark command:
Summary by CodeRabbit
Refactor
Tests