核心内容摘要
69鉂屸潓鉂孒DVideo:沉浸式视听盛宴,开启你的感官新纪元
为什么需要重构起因React Native的坑在将原版relationship.js集成到 React Native 移动端应用时遇到了一个棘手的报错ERROR [RangeError: Property storage exceeds 196607 properties]这是因为原版 JS 在初始化时创建了大量的对象属性超出了JavaScriptCore 引擎的属性数量限制约 196607 个。
技术选型为了彻底解决这个问题我决定从以下几个方向进行重构TypeScript 重写- 提供类型安全和更好的开发体验Map替代 Object- 利用 Map 的特性优化数据存储LRU缓存机制- 提升重复查询性能模块化架构- 更好的代码组织和 Tree Shaking支持核心优化点
数据结构优化Map vs Object原版实现// 使用 Object 存储称谓映射const cacheData { f: [父亲, 老爸, 爹地], m: [母亲, 老妈, 妈咪], // ... 数千个属性 };TS 版本优化class OptimizedCache { private _titleToChain: Mapstring, Chain[] new Map(); private _chainToTitle: Mapstring, string[] new Map(); getTitleToChain(title: string): Chain[] | undefined { return this._titleToChain.get(title); // O(
查找 } }优势Map 提供 O(
的查找性能避免了 Object 的属性数量限制更好的内存管理
LRU 缓存实现这是我这次重构最满意的部分之一。
针对亲戚关系查询场景大量查询是重复的比如爸爸的爸爸这种高频查询非常适合使用缓存。
export class LRUCacheK, V { private capacity: number; private cache: MapK, V; get(key: K): V | undefined { const value this.cache.get(key); if (value ! undefined) { // 更新访问顺序删除并重新插入 - O(
this.cache.delete(key); this.cache.set(key, value); } return value; } }巧妙点利用 Map 的有序性按插入顺序迭代通过delete set实现 O(
的访问顺序更新无需额外的双向链表结构。
分层缓存架构export class QueryCache { private selectorCache: LRUCachestring, string[]; // 中文转选择器 private idCache: LRUCachestring, string[]; // ID转中文 private chainCache: LRUCachestring, string; // 关系链缓存 }三个独立的缓存层针对不同类型的查询结果进行缓存互不干扰。
类型安全export interface RelationshipOptions { text: string; // 目标对象的称谓 target?: string; // 相对对象的称谓 sex?: -1 | 0 | 1; // 本人性别-1未知0女性1男性 type?: default | chain | pair; reverse?: boolean; mode?: string; optimal?: boolean; }完整的 TypeScript 类型定义开发时获得智能提示和类型检查。
性能基准测试测试环境Node.js: v
22.
1
0平台: Windows x64测试方法: 运行 10,000 次重复查询测试脚本/** * 中国亲戚关系计算器性能测试脚本 * 对比原版 JS 和 TypeScript 版本的性能 */ import { performance } from perf_hooks; import fs from fs; // 测试用例 - 涵盖不同复杂度的查询 const testCases [ { text: 父亲, desc: 简单查询 }, { text: 爸爸的妈妈, desc: 两层关系 }, { text: 妈妈的妈妈的哥哥, desc: 三层关系 }, { text: 爸爸的哥哥的妻子的弟弟, desc: 复杂关系链 }, { text: 父亲的父亲的父亲的父亲, desc: 四层关系(缓存) }, ]; // 格式化数字 function formatNumber(num) { return new Intl.NumberFormat(zh-CN).format(num); } // 格式化时间 function formatTime(ms) { if (ms
0.
return ${(ms *
.toFixed(
} μs; if (ms
return ${(ms *
.toFixed(
} ms; return ${ms.toFixed(
} ms; } // 获取文件大小 function getFileSize(filePath) { const stats fs.statSync(filePath); return stats.size; } // 性能测试函数 async function runBenchmark(name, relationshipFunc) { console.log(\n${.repeat(
}); console.log(测试: ${name}); console.log(.repeat(
); const results { coldStart: [], warmStart: [], queries: {}, }; // 预热 - 确保模块初始化 relationshipFunc({ text: 父亲 }); //
冷启动测试- 测试不同查询的首次性能console.log(\n 冷启动测试 (各查询类型的首次性能)...); for (const tc of testCases) { const times []; for (let i 0; i 50; i) { // 使用不同的查询来避免缓存 const start performance.now(); relationshipFunc({ text: tc.text (i 0 ? 的 父亲.repeat(i %
: ) }); const end performance.now(); times.push(end - start); } const avg times.reduce((a, b) a b,
/ times.length; results.queries[tc.desc (冷)] avg; console.log( ${tc.desc.padEnd(
}: ${formatTime(avg)}); } //
热启动测试 - 重复同一查询测试缓存效果 console.log(\n 热启动测试 (重复查询命中缓存)...); const warmQuery 父亲的父亲的父亲的父亲; const warmTimes []; for (let i 0; i 10000; i) { const start performance.now(); relationshipFunc({ text: warmQuery }); const end performance.now(); warmTimes.push(end - start); } const avgWarm warmTimes.reduce((a, b) a b,
/ warmTimes.length; const minWarm Math.min(...warmTimes); const maxWarm Math.max(...warmTimes); // 排除异常值取中位数附近 const sorted [...warmTimes].sort((a, b) a - b); const medianWarm sorted[Math.floor(sorted.length /
]; results.avgWarm avgWarm; results.minWarm minWarm; results.maxWarm maxWarm; results.medianWarm medianWarm; results.qps 1000 / medianWarm; console.log( 平均耗时: ${formatTime(avgWarm)}); console.log( 中位数耗时: ${formatTime(medianWarm)} (去除异常值)); console.log( 最小耗时: ${formatTime(minWarm)}); console.log( 最大耗时: ${formatTime(maxWarm)}); console.log( QPS: ${formatNumber(1000 / medianWarm)} req/s); return results; } // 主测试函数 async function main() { console.log(\n █.repeat(
); console.log(█ .repeat(
性能测试基准测试 .repeat(
█); console.log(█.repeat(
); console.log(\n 测试环境:); console.log( Node.js: ${process.version}); console.log( 平台: ${process.platform} ${process.arch}); console.log( 热启动迭代: 10000 次); // 包体积对比 console.log(\n 包体积对比:); const jsSize getFileSize(E:/github-project/relationship/dist/relationship.min.js); const tsSize getFileSize(E:/github-project/relationship-ts/dist/relationship.min.js); const jsSizeKb (jsSize /
.toFixed(
; const tsSizeKb (tsSize /
.toFixed(
; const diff ((tsSize - jsSize) / jsSize *
.toFixed(
; console.log( 原版 (JS): ${jsSizeKb} KB (${formatNumber(jsSize)} bytes)); console.log( 优化版 (TS): ${tsSizeKb} KB (${formatNumber(tsSize)} bytes)); console.log( 差异: ${diff 0 ? : }${diff}%); // 导入两个版本 console.log(\n 加载模块...); // 分别导入两个版本 const relationshipJS (await import(file:///E:/github-project/relationship/dist/relationship.min.mjs)).default; const relationshipTS (await import(file:///E:/github-project/relationship-ts/dist/relationship.min.mjs)).default; console.log( ✅ 原版 (JS) 加载完成); console.log( ✅ 优化版 (TS) 加载完成); // 运行测试 const jsResults await runBenchmark(原版 (relationship.js), relationshipJS); const tsResults await runBenchmark(优化版 (relationship-ts), relationshipTS); // 生成对比报告 console.log(\n .repeat(
); console.log( 性能对比报告); console.log(.repeat(
); // 冷启动对比 console.log(\n 冷启动对比 (首次查询无缓存):); console.log( ${查询类型.padEnd(
} ${原版.padEnd(
} ${优化版.padEnd(
} ${差异.padEnd(
}); console.log( -.repeat(
); let coldImprovements []; for (const [desc, jsTime] of Object.entries(jsResults.queries)) { if (desc.includes((冷))) { const tsTime tsResults.queries[desc]; const improvement ((jsTime - tsTime) / jsTime *
; coldImprovements.push(improvement); const arrow improvement 0 ? : (improvement 0 ? : ➡️); console.log( ${desc.replace((冷), ).padEnd(
} ${formatTime(jsTime).padEnd(
} ${formatTime(tsTime).padEnd(
} ${arrow} ${(improvement 0 ? : ) improvement.toFixed(
}%); } } // 热启动对比 console.log(\n 热启动对比 (重复查询命中 LRU 缓存):); console.log( 原版中位数: ${formatTime(jsResults.medianWarm)}); console.log( 优化版中位数: ${formatTime(tsResults.medianWarm)}); console.log( 原版 QPS: ${formatNumber(jsResults.qps)} req/s); console.log( 优化版 QPS: ${formatNumber(tsResults.qps)} req/s); const warmImprovement ((jsResults.medianWarm - tsResults.medianWarm) / jsResults.medianWarm *
; const qpsImprovement ((tsResults.qps - jsResults.qps) / jsResults.qps *
; const speedup (jsResults.medianWarm / tsResults.medianWarm).toFixed(
; console.log( 耗时减少: ${warmImprovement 0 ? warmImprovement.toFixed(
: 0}%); console.log( QPS 提升: ${qpsImprovement 0 ? : }${formatNumber(Math.round(qpsImprovement))}%); console.log( 性能倍数: ${speedup}x 更快); //
总结 console.log(\n .repeat(
); console.log(
总结); console.log(.repeat(
); console.log( ✅ 优化版主要改进: • 包体积: ${diff.startsWith(-) ? ⬇️ 减少 Math.abs(diff) % : ⬆️ 增加 diff} • 热启动性能: ${speedup}x 更快 (得益于 LRU 缓存机制) • QPS 提升: ${qpsImprovement 0 ? : }${formatNumber(Math.round(qpsImprovement))}% (从 ${formatNumber(Math.round(jsResults.qps))} 到 ${formatNumber(Math.round(tsResults.qps))} req/s) • 类型安全: ✅ 完整的 TypeScript 类型定义 • 架构优化: ✅ 模块化设计更好的 Tree Shaking 支持 • 兼容性: ✅ 解决 React Native 属性存储超限问题 ); return { jsSizeKb, tsSizeKb, diff, jsQps: jsResults.qps, tsQps: tsResults.qps, speedup, jsMedian: jsResults.medianWarm, tsMedian: tsResults.medianWarm, }; } main().catch(console.error);测试结果指标原版 (JS)优化版 (TS)提升包体积
8
60 KB
7