Skip to content

Commit b4142b5

Browse files
optimize bundle-size & perf
1 parent 0e0a281 commit b4142b5

1 file changed

Lines changed: 95 additions & 83 deletions

File tree

packages/router-core/src/router.ts

Lines changed: 95 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,11 +1248,11 @@ export class RouterCore<
12481248
}
12491249

12501250
emit: EmitFn = (routerEvent) => {
1251-
this.subscribers.forEach((listener) => {
1251+
for (const listener of this.subscribers) {
12521252
if (listener.eventType === routerEvent.type) {
12531253
listener.fn(routerEvent)
12541254
}
1255-
})
1255+
}
12561256
}
12571257

12581258
/**
@@ -1765,24 +1765,24 @@ export class RouterCore<
17651765
}
17661766

17671767
cancelMatches = () => {
1768-
this.stores.pendingMatchesId.state.forEach((matchId) => {
1768+
for (const matchId of this.stores.pendingMatchesId.state) {
17691769
this.cancelMatch(matchId)
1770-
})
1770+
}
17711771

1772-
this.stores.matchesId.state.forEach((matchId) => {
1772+
for (const matchId of this.stores.matchesId.state) {
17731773
if (this.stores.pendingMatchStoresById.has(matchId)) {
1774-
return
1774+
continue
17751775
}
17761776

17771777
const match = this.stores.activeMatchStoresById.get(matchId)?.state
17781778
if (!match) {
1779-
return
1779+
continue
17801780
}
17811781

17821782
if (match.status === 'pending' || match.isFetching === 'loader') {
17831783
this.cancelMatch(matchId)
17841784
}
1785-
})
1785+
}
17861786
}
17871787

17881788
/**
@@ -1915,7 +1915,7 @@ export class RouterCore<
19151915
let nextSearch = fromSearch
19161916
if (opts._includeValidateSearch && this.options.search?.strict) {
19171917
const validatedSearch = {}
1918-
destRoutes.forEach((route) => {
1918+
for (const route of destRoutes) {
19191919
if (route.options.validateSearch) {
19201920
try {
19211921
Object.assign(
@@ -1929,7 +1929,7 @@ export class RouterCore<
19291929
// ignore errors here because they are already handled in matchRoutes
19301930
}
19311931
}
1932-
})
1932+
}
19331933
nextSearch = validatedSearch
19341934
}
19351935

@@ -2093,13 +2093,13 @@ export class RouterCore<
20932093
'__TSR_index',
20942094
'__hashScrollIntoViewOptions',
20952095
] as const
2096-
ignoredProps.forEach((prop) => {
2096+
for (const prop of ignoredProps) {
20972097
;(next.state as any)[prop] = this.latestLocation.state[prop]
2098-
})
2098+
}
20992099
const isEqual = deepEqual(next.state, this.latestLocation.state)
2100-
ignoredProps.forEach((prop) => {
2100+
for (const prop of ignoredProps) {
21012101
delete next.state[prop]
2102-
})
2102+
}
21032103
return isEqual
21042104
}
21052105

@@ -2418,54 +2418,42 @@ export class RouterCore<
24182418
// exitingMatches uses match.id (routeId + params + loaderDeps) so
24192419
// navigating /foo?page=1 → /foo?page=2 correctly caches the page=1 entry.
24202420
let exitingMatches: Array<AnyRouteMatch> | null = null
2421-
2422-
// Lifecycle-hook identity uses routeId only so that navigating between
2423-
// different params/deps of the same route fires onStay (not onLeave+onEnter).
2424-
let hookExitingMatches: Array<AnyRouteMatch> | null = null
2425-
let hookEnteringMatches: Array<AnyRouteMatch> | null = null
2426-
let hookStayingMatches: Array<AnyRouteMatch> | null = null
2421+
let pendingMatches: Array<AnyRouteMatch> = []
2422+
let currentMatches: Array<AnyRouteMatch> = []
2423+
let pendingRouteIds: Set<string> | null = null
2424+
let activeRouteIds: Set<string> | null = null
24272425

24282426
this.batch(() => {
2429-
const pendingMatches =
2430-
this.stores.pendingMatchesSnapshot.state
2431-
const mountPending = pendingMatches.length
2432-
const currentMatches =
2433-
this.stores.activeMatchesSnapshot.state
2434-
2435-
exitingMatches = mountPending
2436-
? currentMatches.filter(
2437-
(match) =>
2438-
!this.stores.pendingMatchStoresById.has(match.id),
2439-
)
2440-
: null
2441-
2442-
// Lifecycle-hook identity: routeId only (route presence in tree)
2443-
// Build routeId sets from pools to avoid derived stores.
2444-
const pendingRouteIds = new Set<string>()
2445-
for (const s of this.stores.pendingMatchStoresById.values()) {
2446-
if (s.routeId) pendingRouteIds.add(s.routeId)
2447-
}
2448-
const activeRouteIds = new Set<string>()
2449-
for (const s of this.stores.activeMatchStoresById.values()) {
2450-
if (s.routeId) activeRouteIds.add(s.routeId)
2427+
pendingMatches = this.stores.pendingMatchesSnapshot.state
2428+
currentMatches = this.stores.activeMatchesSnapshot.state
2429+
2430+
if (pendingMatches.length) {
2431+
activeRouteIds = new Set<string>()
2432+
for (const match of currentMatches) {
2433+
activeRouteIds.add(match.routeId)
2434+
}
2435+
2436+
pendingRouteIds = new Set<string>()
2437+
exitingMatches = []
2438+
2439+
for (const match of pendingMatches) {
2440+
pendingRouteIds.add(match.routeId)
2441+
}
2442+
2443+
for (const match of currentMatches) {
2444+
if (
2445+
!this.stores.pendingMatchStoresById.has(match.id) &&
2446+
match.status !== 'error' &&
2447+
match.status !== 'notFound' &&
2448+
match.status !== 'redirected'
2449+
) {
2450+
exitingMatches.push(match)
2451+
}
2452+
}
2453+
} else {
2454+
exitingMatches = null
24512455
}
24522456

2453-
hookExitingMatches = mountPending
2454-
? currentMatches.filter(
2455-
(match) => !pendingRouteIds.has(match.routeId),
2456-
)
2457-
: null
2458-
hookEnteringMatches = mountPending
2459-
? pendingMatches.filter(
2460-
(match) => !activeRouteIds.has(match.routeId),
2461-
)
2462-
: null
2463-
hookStayingMatches = mountPending
2464-
? pendingMatches.filter((match) =>
2465-
activeRouteIds.has(match.routeId),
2466-
)
2467-
: currentMatches
2468-
24692457
this.stores.isLoading.setState(() => false)
24702458
this.stores.loadedAt.setState(() => Date.now())
24712459
/**
@@ -2474,31 +2462,45 @@ export class RouterCore<
24742462
* deliberately excluded from `cachedMatches` so that subsequent invalidations
24752463
* or reloads re-run their loaders instead of reusing the failed/not-found data.
24762464
*/
2477-
if (mountPending) {
2465+
if (pendingMatches.length) {
24782466
this.stores.setActiveMatches(pendingMatches)
24792467
this.stores.setPendingMatches([])
24802468
this.stores.setCachedMatches([
24812469
...this.stores.cachedMatchesSnapshot.state,
2482-
...exitingMatches!.filter(
2483-
(d) =>
2484-
d.status !== 'error' &&
2485-
d.status !== 'notFound' &&
2486-
d.status !== 'redirected',
2487-
),
2470+
...exitingMatches!,
24882471
])
24892472
this.clearExpiredCache()
24902473
}
24912474
})
24922475

2493-
//
2494-
for (const [matches, hook] of [
2495-
[hookExitingMatches, 'onLeave'],
2496-
[hookEnteringMatches, 'onEnter'],
2497-
[hookStayingMatches, 'onStay'],
2498-
] as const) {
2499-
if (!matches) continue
2500-
for (const match of matches as Array<AnyRouteMatch>) {
2501-
this.looseRoutesById[match.routeId]!.options[hook]?.(
2476+
// Lifecycle-hook identity uses routeId only so that navigating between
2477+
// different params/deps of the same route fires onStay (not onLeave+onEnter).
2478+
const nextPendingRouteIds = pendingRouteIds as Set<string> | null
2479+
const nextActiveRouteIds = activeRouteIds as Set<string> | null
2480+
2481+
if (nextPendingRouteIds && nextActiveRouteIds) {
2482+
for (const match of currentMatches) {
2483+
if (!nextPendingRouteIds.has(match.routeId)) {
2484+
this.looseRoutesById[match.routeId]!.options.onLeave?.(
2485+
match,
2486+
)
2487+
}
2488+
}
2489+
2490+
for (const match of pendingMatches) {
2491+
if (nextActiveRouteIds.has(match.routeId)) {
2492+
this.looseRoutesById[match.routeId]!.options.onStay?.(
2493+
match,
2494+
)
2495+
} else {
2496+
this.looseRoutesById[match.routeId]!.options.onEnter?.(
2497+
match,
2498+
)
2499+
}
2500+
}
2501+
} else {
2502+
for (const match of currentMatches) {
2503+
this.looseRoutesById[match.routeId]!.options.onStay?.(
25022504
match,
25032505
)
25042506
}
@@ -2525,9 +2527,7 @@ export class RouterCore<
25252527
? redirect.status
25262528
: notFound
25272529
? 404
2528-
: this.stores.activeMatchesSnapshot.state.some(
2529-
(d) => d.status === 'error',
2530-
)
2530+
: this.hasErrorMatch()
25312531
? 500
25322532
: 200
25332533

@@ -2561,9 +2561,7 @@ export class RouterCore<
25612561
let newStatusCode: number | undefined = undefined
25622562
if (this.hasNotFoundMatch()) {
25632563
newStatusCode = 404
2564-
} else if (
2565-
this.stores.activeMatchesSnapshot.state.some((d) => d.status === 'error')
2566-
) {
2564+
} else if (this.hasErrorMatch()) {
25672565
newStatusCode = 500
25682566
}
25692567
if (newStatusCode !== undefined) {
@@ -2930,10 +2928,24 @@ export class RouterCore<
29302928

29312929
serverSsr?: ServerSsr
29322930

2931+
hasErrorMatch = () => {
2932+
for (const match of this.stores.activeMatchesSnapshot.state) {
2933+
if (match.status === 'error') {
2934+
return true
2935+
}
2936+
}
2937+
2938+
return false
2939+
}
2940+
29332941
hasNotFoundMatch = () => {
2934-
return this.stores.activeMatchesSnapshot.state.some(
2935-
(d) => d.status === 'notFound' || d.globalNotFound,
2936-
)
2942+
for (const match of this.stores.activeMatchesSnapshot.state) {
2943+
if (match.status === 'notFound' || match.globalNotFound) {
2944+
return true
2945+
}
2946+
}
2947+
2948+
return false
29372949
}
29382950
}
29392951

0 commit comments

Comments
 (0)