v1.6.1 针对 isSameQuery 和 Object.freeze 两处高频执行路径进行优化,减少不必要的计算开销与重复代码
前言
v1.6.0 引入了 params 页面参数传递和查询参数增强方法,功能更加完整。但在实际使用中,路由状态管理存在两处可优化的细节:
v1.6.1 针对这两处进行优化,在不改变任何外部行为的前提下提升性能和可维护性。
一、isSameQuery 空对象快速路径
问题分析
isSameQuery 在路由器内部被高频调用:
- setCurrentRoute 中判断 from 和 to 是否相同
- syncCurrentRoute 中比较当前查询参数
- isSameRouteLocation 中作为路由位置比较的一部分
原始实现无论查询参数是否为空对象,都会执行 Object.keys() 和 every() 遍历:
// 优化前
private isSameQuery(a: Record<string, string>, b: Record<string, string>): boolean {
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
return keysA.every(key => a[key] === b[key])
}
在空对象场景下(如首页导航、无参数跳转),Object.keys({}) 返回空数组,every 对空数组返回 true,虽然结果正确,但产生了不必要的函数调用开销。
优化方案
添加两层快速路径:
// 优化后
private isSameQuery(a: Record<string, string>, b: Record<string, string>): boolean {
if (a === b) return true // 快速路径 1:引用相等
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
if (keysA.length === 0) return true // 快速路径 2:双空对象
return keysA.every(key => a[key] === b[key])
}
快速路径 1:引用相等(a === b)
当 a 和 b 是同一个对象引用时,直接返回 true。这种情况在路由状态未变化时(如重复导航检测)经常出现。
快速路径 2:双空对象(keysA.length === 0)
当 keysA.length === keysB.length 且 keysA.length === 0 时,说明两者都是空对象,直接返回 true。避免了 every 对空数组的调用。
性能影响
| 引用相等 | Object.keys + every | 直接返回 true |
| 双空对象 | Object.keys × 2 + every | Object.keys × 2 + 长度判断 |
| 非空对象 | 不受影响 | 不受影响 |
对于高频调用的导航场景(特别是无参数跳转),减少了不必要的 Object.keys 和 every 开销。
二、Object.freeze 逻辑集中化
问题分析
v1.6.0 中,meta、query、params 的冻结逻辑分散在两处:
// 优化前:setCurrentRoute 中
function setCurrentRoute(route: RouteLocation): void {
const from = currentRoute
currentRoute = {
path: route.path,
name: route.name,
meta: Object.freeze({ …route.meta }), // 手动冻结
query: Object.freeze({ …route.query }), // 手动冻结
params: Object.freeze({ …route.params })
// …
}
}
// 优化前:createStartLocation 中
export function createStartLocation(): RouteLocation {
return {
path: '/',
meta: Object.freeze({}), // 手动冻结
query: Object.freeze({}), // 手动冻结
params: Object.freeze({}) // 手动冻结
// …
}
}
这种分散存在两个问题:
优化方案
将冻结逻辑集中到 createRouteLocation 工厂函数中:
// 优化后:createRouteLocation 中统一冻结
export function createRouteLocation(base: { path: string; name?: string; meta: RouteMeta; query: Record<string, string>; fullPath: string; params?: ParamObject; _synced?: boolean }): RouteLocation {
const query = Object.freeze(base.query)
const params: Readonly<ParamObject> = base.params ? Object.freeze({ …base.params }) : Object.freeze({})
return {
path: base.path,
name: base.name,
meta: Object.freeze({ …base.meta }),
query,
params
// …
}
}
setCurrentRoute 和 createStartLocation 不再手动冻结,统一委托给 createRouteLocation:
// 优化后:setCurrentRoute 中
function setCurrentRoute(route: RouteLocation): void {
const from = currentRoute
currentRoute = createRouteLocation({
path: route.path,
name: route.name,
meta: { …route.meta },
query: { …route.query },
fullPath: route.fullPath,
params: route.params,
…(route._synced !== undefined && { _synced: route._synced })
})
// …
}
// 优化后:createStartLocation 中
export function createStartLocation(): RouteLocation {
return createRouteLocation({
path: '/',
meta: {},
query: {},
fullPath: '/'
})
}
优化收益
| 冻结逻辑位置 | setCurrentRoute + createStartLocation | createRouteLocation 一处 |
| 代码行数 | 分散在多处 | 集中在工厂函数 |
| 后续维护 | 改冻结策略需改多处 | 只需改 createRouteLocation |
| 一致性 | 手动保证 | 工厂函数自动保证 |
升级指南
v1.6.1 是纯内部优化,完全向后兼容,无需修改任何现有代码即可升级。
本次优化不改变任何公共 API 的行为和签名,所有变更仅限于内部实现细节。如果你正在使用 v1.6.0,直接升级到 v1.6.1 即可享受性能提升。


