JavaScript性能优化需围绕执行效率、内存占用和响应及时性做取舍,核心是避免主线程阻塞、减少GC压力、按需加载及测量先行。
JavaScript 性能优化不是“加个 use strict 就快了”,而是围绕执行效率、内存占用和响应及时性做有针对性的取舍。多数卡顿问题出在主线程被长时间阻塞,或无意中创建了大量临时对象。
浏览器主线程既要处理 JS 执行,也要负责渲染、响应事件。一个超过 50ms 的同步任务(比如遍历 10 万条数据并频繁 DOM 操作)就会导致掉帧甚至卡死。
setTimeout 或 requestIdleCallback 拆分耗时逻辑,让出控制权给渲染for 循环而非 forEach(避免函数调用开销和闭包捕获)DocumentFragment 或一次性设置 innerHTML,而不是反复 appen
dChild
V8 的垃圾回收(GC)虽快,但频繁触发仍会导致明显停顿。常见诱因是短生命周期对象暴增,比如在循环里反复创建对象或字符串拼接。
new 一个join 或模板字面量,少用 +=(尤其在循环中)addEventListener 时尽量传入 { once: true }
首屏不需要的逻辑,就别让它进主包。体积大 ≠ 运行慢,但体积大会拖慢解析、编译和首次执行时间。
dynamic import() 实现路由级或组件级懒加载,Webpack/Vite 会自动切 chunklodash-es 中解构 debounce,而非整个 lodash
有些写法看着干净,实则代价很高,且不易被察觉。
console.log 在生产环境未关闭时,可能序列化大型对象并阻塞主线程for...in 遍历对象会遍历原型链,应优先用 Object.keys(obj).forEach 或 for...of + Object.entries
offsetHeight、getBoundingClientRect() 会强制触发回流(reflow),应缓存结果或用 ResizeObserver/IntersectionObserver 替代轮询const expensiveList = new Array(100000).fill(0);
// ❌ 卡住主线程
expensiveList.forEach((_, i) => {
document.body.innerHTML += `${i}`;
});
// ✅ 拆分 + 批量插入
function renderChunk(start, end) {
const fragment = document.createDocumentFragment();
for (let i = start; i < end && i < expensiveList.length; i++) {
const div = document.createElement('div');
div.textContent = i;
fragment.appendChild(div);
}
document.body.appendChild(fragment);
}
renderChunk(0, 1000); // 第一批
setTimeout(() => renderChunk(1000, 2000), 0);
最常被忽略的是「测量先行」——不靠直觉,用 Chrome DevTools 的 Performance 面板录制真实用户操作路径,看火焰图里哪一帧耗时异常、GC 是否密集、是否有 layout thrashing。没有 profile 数据的优化,大概率是在改错地方。