在优化Javascript代码性能时,关注页面渲染效率和代码执行效率尤为重要。
减少重回(Repaint)和重排(Reflow)
背景知识
-
重排(Reflow):DOM结构或样式变化导致布局重新计算。
- 触发条件:
- 添加、删除DOM元素。
- 元素的几何属性(如宽高、边距、内边距、边框、定位等)变化。
- 浏览器窗口大小调整。
- 修改CSS样式,如
display,width,height等。
- 性能消耗: 重排需要重新计算布局和渲染树,性能开销较大,尤其是在复杂页面中会引起整棵DOM树的重新布局。
- 触发条件:
-
重绘(Repaint):元素外观(如颜色)变化,不影响布局。
- 触发条件:
- 修改
color,background-color,visibility等不改变几何属性的样式。
- 修改
- 性能消耗:相比重排,重绘的性能开销较小,因为不涉及布局的重新计算。
- 触发条件:
-
重排比重绘消耗更多性能,频繁触发会导致页面卡顿。
优化策略
- 批量修改DOM:
- 合并多次对DOM的操作
- 使用
DocumentFragment或隐藏元素后再修改
- 避免出发多次布局计算:
- 缓存布局信息(如
offsetHeight、getBoundingClientRect) - 尽量减少样式的读取和写入交替操作
- 缓存布局信息(如
使用节流(Throttle)和防抖(Debounce)技术
背景知识
- 节流(Throttle):规定在一定时间间隔内只执行一次函数。
- 防抖(Debounce):在事件连续触发时,延迟执行函数,直到触发结束。
使用场景
- 节流:滚动事件、窗口大小调整
- 防抖:搜索框输入、按钮点击
节流函数:
function throttle(fn, delay) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
fn.apply(this, args);
}
};
}
防抖函数:
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
使用性能分析工具
工具推荐
- 浏览器开发者工具:
- Performance面板:
- 功能简介:
- Performance面板是浏览器开发者工具中的一部分,用于分析页面加载和渲染的性能。
- 通过记录和可视化浏览器执行过程,帮助开发者了解页面的性能瓶颈。
- 关键功能:
- 页面加载分析:查看页面从加载到完全渲染的全过程,包括
DOMContentLoaded和Load事件的触发时机。 - 帧率检测:可通过帧率图判断页面是否流畅,找出导致帧率下降的原因。
- 重排与重绘:详细显示哪些操作触发了回流(Layout)和重绘(Paint),帮助开发者减少不必要的性能消耗。
- JavaScript执行时间:显示脚本执行时间,识别耗时长的函数。
- 页面加载分析:查看页面从加载到完全渲染的全过程,包括
- 功能简介:
- Lighthouse:
- 功能简介:
- Lighthouse是Chrome内置的一款自动化性能分析 工具,可以生成网页的性能报告,并提供详细的优化 建议。
- 适合用来检查页面的整体性能、可访问性和SEO。
- 关键功能:
- 性能评分:根据核心性能指标(如页面加载速度、交互性等)生成性能评分。
- 问题定位与建议:提供具体的性能问题描述及解决建议,例如优化图片、减少无效的CSS和JS。
- 渐进式Web应用(PWA)检测:检查页面是否满足PWA标准。
- 可访问性与最佳实践:分析页面对不同用户的友好程度,如对视障用户的支持等。
- 功能简介:
- Performance面板:
- 第三方库:
- web-vitals:监控网站核心性能指标
- Fps Meter:监控帧率(FPS)
其他优化技巧
- 使用Web Worker:
- 将计算密集型任务从主线程分离,避免阻塞页面渲染
const worker = new Worker('worker.js'); worker.postMessage('start'); worker.onmessage = (e) => { console.log('结果:', e.data); }; - 延迟加载资源:
- 图片懒加载:
<img loading='lazy' src='example.jpg'/> - 动态加载模块:
import()
- 图片懒加载:
- 减少不必要的事件监听:
- 使用事件委托,避免对子元素逐一绑定事件
document.getElementById('parent').addEventListener('click', (e) => { if (e.target.matches('.child')) { console.log('子元素点击'); } }); - 代码分片:
- 使用
requestIdleCallback执行非紧急任务
requestIdleCallback(() => { console.log('页面空闲时执行任务'); }); - 使用
通过以上方法,结合场景需求对代码进行优化,可以显著提高JavaScript的性能和用户体验。