JavaScript 应用性能优化:实战篇
JavaScript 应用的性能不仅仅是锦上添花,更是影响用户体验(UX)、转化率和基础设施成本的关键因素。页面加载缓慢或 API 响应延迟,可能导致高达 40% 的流量损失。本文将介绍 JavaScript(Node.js 环境)代码优化的实用方法,帮助您让应用更快、更高效。我们不会深入探讨 Event Loop 的理论基础,而是直接进入具体的技术和示例。
1. 缓存:减轻服务器负载
加速应用最有效的方法之一,就是避免重复执行昂贵的操作。缓存可以保存计算结果或数据库查询的结果,并在后续请求中直接返回。
使用 Map 进行内存缓存
对于不需要复杂失效逻辑的简单场景,内置的 Map 对象是理想选择。它比 Object 更快,更适合频繁添加和删除键值对。
const cache = new Map();
async function fetchUserData(userId) { if (cache.has(userId)) { console.log('从缓存返回'); return cache.get(userId); }
// 模拟昂贵的数据库查询 const data = await new Promise((resolve) => { setTimeout(() => resolve({ id: userId, name: `User_${userId}` }), 1000); });
cache.set(userId, data); return data;}
// 第一次调用(等待 1 秒)console.time('first');await fetchUserData(1);console.timeEnd('first');
// 第二次调用(瞬间)console.time('second');await fetchUserData(1);console.timeEnd('second');TTL(生存时间)缓存
为了防止缓存过期,需要为记录添加生存时间。下面实现一个简单的 TTL 缓存。
class TTLMap { constructor(ttlMs = 60000) { this.ttl = ttlMs; this.store = new Map(); }
set(key, value) { const record = { value, expiry: Date.now() + this.ttl }; this.store.set(key, record); }
get(key) { const record = this.store.get(key); if (!record) return null; if (Date.now() > record.expiry) { this.store.delete(key); return null; } return record.value; }}
const userCache = new TTLMap(5000); // 缓存 5 秒2. 异步与 Event Loop 优化
Node.js 是单线程的,但其异步特性使其能够高效处理大量请求。性能的主要敌人是阻塞 Event Loop 的同步操作。
避免使用函数的同步版本
fs、crypto 等模块都有同步版本(readFileSync、crypto.pbkdf2Sync)。仅在应用初始化时使用它们。在请求处理程序中,只使用异步版本。
// 错误:阻塞 Event Loopconst data = fs.readFileSync('/path/to/file.txt', 'utf-8');
// 正确:异步const data = await fs.promises.readFile('/path/to/file.txt', 'utf-8');使用 setImmediate 分解繁重任务
如果您有一个繁重的同步循环(例如,处理包含 1000 万个元素的数组),它会阻塞服务器数秒。将任务分解成小块,并使用 setImmediate 让 Event Loop 在块之间处理其他请求。
function processLargeArray(items, chunkSize = 1000) { let index = 0;
function processChunk() { const chunk = items.slice(index, index + chunkSize); // 处理块 chunk.forEach(item => heavyOperation(item));
index += chunkSize; if (index < items.length) { // 在 Event Loop 的下一次迭代中继续