如何通过优化JavaScript代码来提高性能?
1.减少重绘和重排
重绘和重排是影响页面性能的重要因素,所以优化这些操作可以显著地提高性能。下面来列举一些减少重绘和重排地优化操作。
- 合并DOM操作:避免频繁地操作DOM,使用documentFragment或innerHTMl一次性更新多个元素。
- 减少样式计算的影响:
- 将样式的改动放到一个操作中。
- 使用类替代内联样式进行批量更新(class)
- 避免触发同步布局事件:
- 在实践过程中要尽量避免读取导致浏览器强制计算布局的属性,比如offsetHeight、scrollTop等,特别是写入DOM后立即读取。
- 缓存这些属性值,能减少重复计算。
- 使用CSS动画替代JavaScript动画:CSS动画是通过利用GPU进行加速的,行呢个通常比JavaScript动画要好。
2.节流
- 一段时间内只允许函数执行一次,即使事件不断触发,也会限制函数的调用频率。
- 节流在固定时间间隔内忽略掉多次触发,只执行最后一次有效调用。
例如页面滚动,滚动页面时频繁触发scroll事件,节流可以让事件处理逻辑每隔一段时间执行一次。
按钮点击:防止用户快速多次点击按钮而导致重复提交。
function throttle(fn, interval) {
let lastTime = 0; // 上一次执行的时间戳
return function(...args) {
const now = Date.now(); // 当前时间
if (now - lastTime >= interval) {
lastTime = now; // 更新上次执行时间
fn.apply(this, args); // 执行函数
}
};
}
// 示例:节流的滚动事件
window.addEventListener('scroll', throttle(() => {
console.log('滚动事件触发');
}, 200));
特点:
- 在一定时间间隔内函数只执行一次。
- 不管事件触发有多频繁,函数的调用是有规律的。
3.防抖
确保回调在一定时间内只执行一次。
一段时间内如果事件不断被触发,函数执行被延迟到最后一次触发事件后的特定时间后。如果在等待的时间内事件再次出发,计时器会重置,重新计时。
例如,在用户停止调整窗口大小后,在执行调整布局的逻辑。
另外一个例子,输入框:用户在输入时,不需要每输入一个字符都向服务器发送请求,而是等用户停止输入一段时间后再发送请求。
function debounce(fn, delay) {
let timer; // 保存定时器
return function(...args) {
const context = this;
clearTimeout(timer); // 每次触发都清除上一次的定时器
timer = setTimeout(() => fn.apply(context, args), delay); // 重新设置定时器
};
}
// 示例:防抖的搜索输入
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(() => {
console.log('发送搜索请求');
}, 500));
特点:
- 在事件停止后一定时间后执行函数。
- 多次触发时函数只执行一次。
| 对比项 | 防抖 | 节流 |
|---|---|---|
| 触发条件 | 等到事件触发后的一段时间内没有再次触发时,执行函数。 | 在固定时间间隔内只执行一次函数,忽略中间触发。 |
| 使用场景 | 用户操作频繁,希望减少不必要的函数调用,如搜索输入提示。 | 高频触发事件中需要定时执行的操作,如滚动、拖拽等。 |
| 触发频率 | 最多触发一次(停止触发后的延迟时间)。 | 固定时间间隔触发一次函数(间隔时间内忽略触发事件)。 |
| 适用事件 | input、resize 等用户停止操作后的逻辑处理。 | scroll、mousemove等需定时处理的逻辑。 |
| 实现复杂度 | 需要使用定时器重置逻辑,稍复杂。 | 时间判断逻辑,较为简单。 |
4.使用性能分析工具
我们可以借助浏览器开发者共和第三方工具来找性能瓶颈。
- Chrome开发者工具
- 使用Performance面板分析页面加载、渲染和脚本执行的时间。
- 使用Memory面板检测内存泄露和内存使用情况。
- 等等。。。
5.其他
异步加载与懒加载
- 异步加载资源:使用async和defer加载
- 懒加载图片资源:
- 使用原生loading="lazy"或第三方库(比如lazysizes)实现图片懒加载