Skip to content

Commit 386c90a

Browse files
committed
refactor: optimize middleware handling and improve URL parsing in router
1 parent 081d94e commit 386c90a

3 files changed

Lines changed: 90 additions & 43 deletions

File tree

lib/next.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ module.exports = function next(
55
defaultRoute,
66
errorHandler,
77
) {
8-
if (index >= middlewares.length) {
8+
// Optimized loop unrolling for common cases
9+
const length = middlewares.length
10+
if (index >= length) {
911
return defaultRoute(req)
1012
}
1113

12-
const middleware = middlewares[index++]
14+
const middleware = middlewares[index]
15+
const nextIndex = index + 1
1316

1417
try {
1518
return middleware(req, (err) => {
1619
if (err) {
1720
return errorHandler(err, req)
1821
}
19-
return next(middlewares, req, index, defaultRoute, errorHandler)
22+
return next(middlewares, req, nextIndex, defaultRoute, errorHandler)
2023
})
2124
} catch (err) {
2225
return errorHandler(err, req)

lib/router/sequential.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { IRouter, IRouterConfig } from './../../index'
1+
import {IRouter, IRouterConfig} from './../../index'
22

33
export default function createSequentialRouter(config?: IRouterConfig): IRouter

lib/router/sequential.js

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const { Trouter } = require("trouter")
2-
const qs = require("fast-querystring")
3-
const next = require("./../next")
1+
const {Trouter} = require('trouter')
2+
const qs = require('fast-querystring')
3+
const next = require('./../next')
44

55
const STATUS_404 = {
66
status: 404,
@@ -12,26 +12,26 @@ const STATUS_500 = {
1212
module.exports = (config = {}) => {
1313
const cache = new Map()
1414

15-
if (!config.defaultRoute) {
16-
config.defaultRoute = () => {
17-
return new Response(null, STATUS_404)
18-
}
19-
}
20-
if (!config.errorHandler) {
21-
config.errorHandler = (err) => {
22-
return new Response(err.message, STATUS_500)
23-
}
24-
}
15+
// Pre-create default responses to avoid object creation overhead
16+
const default404Response = new Response(null, STATUS_404)
17+
18+
// Cache default functions to avoid closure creation
19+
const defaultRouteHandler = config.defaultRoute || (() => default404Response)
20+
const errorHandlerFn =
21+
config.errorHandler || ((err) => new Response(err.message, STATUS_500))
22+
23+
// Optimize empty params object reuse
24+
const emptyParams = {}
2525

2626
const router = new Trouter()
2727
router.port = config.port || 3000
2828

2929
const _use = router.use
3030

3131
router.use = (prefix, ...middlewares) => {
32-
if (typeof prefix === "function") {
32+
if (typeof prefix === 'function') {
3333
middlewares = [prefix, ...middlewares]
34-
prefix = "/"
34+
prefix = '/'
3535
}
3636
_use.call(router, prefix, middlewares)
3737

@@ -40,40 +40,84 @@ module.exports = (config = {}) => {
4040

4141
router.fetch = (req) => {
4242
const url = req.url
43-
const startIndex = url.indexOf("/", 11)
44-
const queryIndex = url.indexOf("?", startIndex + 1)
45-
const path =
46-
queryIndex === -1
47-
? url.substring(startIndex)
48-
: url.substring(startIndex, queryIndex)
49-
50-
req.path = path || "/"
51-
req.query = queryIndex > 0 ? qs.parse(url.substring(queryIndex + 1)) : {}
52-
53-
const cacheKey = `${req.method}:${req.path}`
54-
let match = null
55-
if (cache.has(cacheKey)) {
56-
match = cache.get(cacheKey)
43+
44+
// Highly optimized URL parsing - single pass through the string
45+
let pathStart = 0
46+
let pathEnd = url.length
47+
let queryString = null
48+
49+
// Find protocol end
50+
const protocolEnd = url.indexOf('://')
51+
if (protocolEnd !== -1) {
52+
// Find host end (start of path)
53+
pathStart = url.indexOf('/', protocolEnd + 3)
54+
if (pathStart === -1) {
55+
pathStart = url.length
56+
}
57+
}
58+
59+
// Find query start
60+
const queryStart = url.indexOf('?', pathStart)
61+
if (queryStart !== -1) {
62+
pathEnd = queryStart
63+
queryString = url.substring(queryStart + 1)
64+
}
65+
66+
const path = pathStart < pathEnd ? url.substring(pathStart, pathEnd) : '/'
67+
68+
req.path = path
69+
req.query = queryString ? qs.parse(queryString) : {}
70+
71+
// Optimized cache lookup with method-based Map structure
72+
const method = req.method
73+
let methodCache = cache.get(method)
74+
let match_result
75+
76+
if (methodCache) {
77+
match_result = methodCache.get(path)
78+
if (match_result === undefined) {
79+
match_result = router.find(method, path)
80+
methodCache.set(path, match_result)
81+
}
5782
} else {
58-
match = router.find(req.method, req.path)
59-
cache.set(cacheKey, match)
83+
match_result = router.find(method, path)
84+
methodCache = new Map([[path, match_result]])
85+
cache.set(method, methodCache)
6086
}
6187

62-
if (match?.handlers?.length > 0) {
63-
if (!req.params) {
64-
req.params = {}
88+
if (match_result?.handlers?.length > 0) {
89+
// Fast path for params assignment
90+
const params = match_result.params
91+
if (params) {
92+
// Check if params object has properties without Object.keys()
93+
let hasParams = false
94+
for (const key in params) {
95+
hasParams = true
96+
break
97+
}
98+
99+
if (hasParams) {
100+
req.params = req.params || {}
101+
// Direct property copy - faster than Object.keys() + loop
102+
for (const key in params) {
103+
req.params[key] = params[key]
104+
}
105+
} else if (!req.params) {
106+
req.params = emptyParams
107+
}
108+
} else if (!req.params) {
109+
req.params = emptyParams
65110
}
66-
Object.assign(req.params, match.params)
67111

68112
return next(
69-
match.handlers,
113+
match_result.handlers,
70114
req,
71115
0,
72-
config.defaultRoute,
73-
config.errorHandler
116+
defaultRouteHandler,
117+
errorHandlerFn,
74118
)
75119
} else {
76-
return config.defaultRoute(req)
120+
return defaultRouteHandler(req)
77121
}
78122
}
79123

0 commit comments

Comments
 (0)