Skip to content

Commit b894ef7

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

File tree

1 file changed

+47
-18
lines changed

1 file changed

+47
-18
lines changed

packages/router/src/matcher/index.ts

+47-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,51 @@ 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(
524+
matcher: RouteRecordMatcher,
525+
matchers: RouteRecordMatcher[]
526+
) {
527+
const depth = getDepth(matcher)
528+
529+
let lower = 0
530+
let upper = matchers.length
531+
532+
while (lower !== upper) {
533+
const mid = (lower + upper) >> 1
534+
const sortOrder =
535+
comparePathParserScore(matcher, matchers[mid]) ||
536+
getDepth(matchers[mid]) - depth
537+
538+
if (sortOrder === 0) {
539+
return mid
540+
} else if (sortOrder < 0) {
541+
upper = mid
542+
} else {
543+
lower = mid + 1
544+
}
545+
}
546+
547+
return upper
548+
}
549+
550+
function getDepth(record: RouteRecordMatcher) {
551+
let depth = 0
552+
553+
while (record.parent) {
554+
++depth
555+
record = record.parent
556+
}
557+
558+
return depth
530559
}
531560

532561
export type { PathParserOptions, _PathParserOptions }

0 commit comments

Comments
 (0)