v1.6.1 针对
isSameQuery和Object.freeze两处高频执行路径进行优化,减少不必要的计算开销与重复代码
前言
v1.6.0 引入了 params 页面参数传递和查询参数增强方法,功能更加完整。但在实际使用中,路由状态管理存在两处可优化的细节:
isSameQuery缺少空对象快速路径 --- 每次导航、守卫执行、状态同步都会调用isSameQuery,而空对象是常见场景Object.freeze逻辑分散 ---meta、query、params的冻结逻辑分散在setCurrentRoute和createStartLocation中,存在重复代码
v1.6.1 针对这两处进行优化,在不改变任何外部行为的前提下提升性能和可维护性。
一、isSameQuery 空对象快速路径
问题分析
isSameQuery 在路由器内部被高频调用:
setCurrentRoute中判断from和to是否相同syncCurrentRoute中比较当前查询参数isSameRouteLocation中作为路由位置比较的一部分
原始实现无论查询参数是否为空对象,都会执行 Object.keys() 和 every() 遍历:
typescript
// 优化前
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,虽然结果正确,但产生了不必要的函数调用开销。
优化方案
添加两层快速路径:
typescript
// 优化后
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--- 手动冻结meta和querycreateStartLocation--- 手动创建冻结的初始对象
typescript
// 优化前: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 工厂函数中:
typescript
// 优化后: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:
typescript
// 优化后: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 即可享受性能提升。