Skip to content

Commit 7255050

Browse files
committed
perf: use a binary search for insertMatcher
1 parent 3025e82 commit 7255050

File tree

1 file changed

+42
-18
lines changed

1 file changed

+42
-18
lines changed

packages/router/src/matcher/index.ts

+42-18
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,8 @@ export function createRouterMatcher(
223223
}
224224

225225
function insertMatcher(matcher: RouteRecordMatcher) {
226-
let i = 0
227-
while (
228-
i < matchers.length &&
229-
comparePathParserScore(matcher, matchers[i]) >= 0 &&
230-
// Adding children with empty path should still appear before the parent
231-
// https://github.com/vuejs/router/issues/1124
232-
(matcher.record.path !== matchers[i].record.path ||
233-
!isRecordChildOf(matcher, matchers[i]))
234-
)
235-
i++
236-
matchers.splice(i, 0, matcher)
226+
const index = findInsertionIndex(matcher, matchers)
227+
matchers.splice(index, 0, matcher)
237228
// only add the original record to the name map
238229
if (matcher.record.name && !isAliasRecord(matcher))
239230
matcherMap.set(matcher.record.name, matcher)
@@ -520,13 +511,46 @@ function checkMissingParamsInAbsolutePath(
520511
}
521512
}
522513

523-
function isRecordChildOf(
524-
record: RouteRecordMatcher,
525-
parent: RouteRecordMatcher
526-
): boolean {
527-
return parent.children.some(
528-
child => child === record || isRecordChildOf(record, child)
529-
)
514+
/**
515+
* Performs a binary search to find the correct insertion index for a new matcher.
516+
*
517+
* Matchers are primarily sorted by their score. If scores are tied then the matcher's depth is used instead.
518+
* The depth check ensures that a child with an empty path comes before its parent.
519+
*
520+
* @param matcher - new matcher to be inserted
521+
* @param matchers - existing matchers
522+
*/
523+
function findInsertionIndex(matcher: RouteRecordMatcher, matchers: RouteRecordMatcher[]) {
524+
const depth = getDepth(matcher)
525+
526+
let lower = 0
527+
let upper = matchers.length
528+
529+
while (lower !== upper) {
530+
const mid = (lower + upper) >> 1
531+
const sortOrder = comparePathParserScore(matcher, matchers[mid]) || getDepth(matchers[mid]) - depth
532+
533+
if (sortOrder === 0) {
534+
return mid
535+
} else if (sortOrder < 0) {
536+
upper = mid
537+
} else {
538+
lower = mid + 1
539+
}
540+
}
541+
542+
return upper
543+
}
544+
545+
function getDepth(record: RouteRecordMatcher) {
546+
let depth = 0
547+
548+
while (record.parent) {
549+
++depth
550+
record = record.parent
551+
}
552+
553+
return depth
530554
}
531555

532556
export type { PathParserOptions, _PathParserOptions }

0 commit comments

Comments
 (0)