Skip to content

Commit

Permalink
perf: use a binary search for insertMatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
skirtles-code committed Feb 10, 2024
1 parent 3025e82 commit 7255050
Showing 1 changed file with 42 additions and 18 deletions.
60 changes: 42 additions & 18 deletions packages/router/src/matcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,8 @@ export function createRouterMatcher(
}

function insertMatcher(matcher: RouteRecordMatcher) {
let i = 0
while (
i < matchers.length &&
comparePathParserScore(matcher, matchers[i]) >= 0 &&
// Adding children with empty path should still appear before the parent
// https://github.com/vuejs/router/issues/1124
(matcher.record.path !== matchers[i].record.path ||
!isRecordChildOf(matcher, matchers[i]))
)
i++
matchers.splice(i, 0, matcher)
const index = findInsertionIndex(matcher, matchers)
matchers.splice(index, 0, matcher)
// only add the original record to the name map
if (matcher.record.name && !isAliasRecord(matcher))
matcherMap.set(matcher.record.name, matcher)
Expand Down Expand Up @@ -520,13 +511,46 @@ function checkMissingParamsInAbsolutePath(
}
}

function isRecordChildOf(
record: RouteRecordMatcher,
parent: RouteRecordMatcher
): boolean {
return parent.children.some(
child => child === record || isRecordChildOf(record, child)
)
/**
* Performs a binary search to find the correct insertion index for a new matcher.
*
* Matchers are primarily sorted by their score. If scores are tied then the matcher's depth is used instead.
* The depth check ensures that a child with an empty path comes before its parent.
*
* @param matcher - new matcher to be inserted
* @param matchers - existing matchers
*/
function findInsertionIndex(matcher: RouteRecordMatcher, matchers: RouteRecordMatcher[]) {
const depth = getDepth(matcher)

let lower = 0
let upper = matchers.length

while (lower !== upper) {
const mid = (lower + upper) >> 1
const sortOrder = comparePathParserScore(matcher, matchers[mid]) || getDepth(matchers[mid]) - depth

if (sortOrder === 0) {
return mid
} else if (sortOrder < 0) {
upper = mid
} else {
lower = mid + 1
}
}

return upper
}

function getDepth(record: RouteRecordMatcher) {
let depth = 0

while (record.parent) {
++depth
record = record.parent
}

return depth
}

export type { PathParserOptions, _PathParserOptions }

0 comments on commit 7255050

Please sign in to comment.