V1.6.1性能优化:高频路径提速与代码精简

v1.6.1 针对 isSameQuery 和 Object.freeze 两处高频执行路径进行优化,减少不必要的计算开销与重复代码

前言

v1.6.0 引入了 params 页面参数传递和查询参数增强方法,功能更加完整。但在实际使用中,路由状态管理存在两处可优化的细节:

  1. isSameQuery 缺少空对象快速路径 --- 每次导航、守卫执行、状态同步都会调用 isSameQuery,而空对象是常见场景
  2. 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 的冻结逻辑分散在两处:

  1. setCurrentRoute --- 手动冻结 meta 和 query
  2. createStartLocation --- 手动创建冻结的初始对象
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({}) // 手动冻结
		// ...
	}
}

这种分散存在两个问题:

  1. 重复代码 --- 冻结逻辑在多处重复,后续若需条件冻结(如开发模式跳过冻结),需改多处
  2. 不一致风险 --- 新增字段时容易遗漏冻结

优化方案

将冻结逻辑集中到 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 即可享受性能提升。

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95274 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大4 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师4 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学4 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端