Repository navigation
Conversation
`getControllerForElementAndIdentifier` copied and scanned every connected context of the identifier, and matching an element against an outlet selector scanned every connected outlet. Outlets do both for each matching element, so a list of N outlets cost O(N²) to connect. Index connected contexts by element, and check outlet elements by membership. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Connecting a list of outlets is quadratic, because two lookups scan everything already connected:
Router#getContextForElementAndIdentifier, behindgetControllerForElementAndIdentifierandOutletObserver#getOutlet, copied every connected context of the identifier into an array and searched it for the element.OutletObserver#hasOutletthen scanned every connected outlet of that name, throughgetOutletFromMap, whenever the element had no connected controller yet.Outlets run both for each element matching the outlet selector, so a list of 1,000 rows declared as outlets of a parent controller took 1.4 s to start on a fast laptop, and a list of 3,000 more than 30 s.
Modulenow keeps its connected contexts in aMapkeyed by element (an element has at most one context per module, sinceScopeObservermaps an element and identifier to a single scope), andhasOutletchecksoutletElementsByName, whichconnectOutletanddisconnectOutletkeep in step withoutletsByName. Ordering ofapplication.controllersis unchanged.What remains is
OutletObserver#startrefreshing the parent's selector observers, a fullquerySelectorAllof its subtree, each time one outlet connects. Fixing that needs a per-element refresh in the observers, so I left it out of this PR.Two tests count the reads of
Module#contextsand of the outlets map, and fail onmain. Sibling PR: #925, which adds a test at the same place inapplication_tests.ts, so whichever lands second has a trivial conflict.Measurements
Application#starton a page where alistcontroller declaresstatic outlets = ["row"], withdata-list-row-outlet="[data-controller=row]"around Nrowcontrollers, each with 2 targets, 2 values, 1 class and a button with 2 actions.mainat 4145d26 and this branch, bundled with esbuild, Chrome 156 on an Apple Silicon Mac, median of 9 page loads.mainWould a per-element refresh for the remaining part be welcome as a follow-up?
🤖 Generated with Claude Code, but prompted, read, and verified by @czj, a caring human :)