前端性能优化中的技能CD和回城
🚀 前端性能优化必备技巧:深入理解防抖与节流
📚 前言
在前端开发中,性能优化是一个永恒的主题。当我们处理高频触发事件时,如果不进行适当处理,可能会导致以下问题:
- 🔥 频繁触发事件导致性能下降
- 💾 不必要的服务器请求
- 🖥️ 页面卡顿
- ⚡ 资源浪费
🎯 常见的高频触发场景
-
搜索框实时搜索 🔍
- 用户输入时频繁发起请求
- 每次按键都触发搜索
-
窗口调整事件 📱
- resize 事件频繁触发
- 需要重新计算布局
-
滚动事件处理 📜
- scroll 事件持续触发
- 可能影响页面性能
-
按钮提交事件 🖱️
- 用户重复点击提交
- 可能导致重复请求
🛡️ 防抖(Debounce)
🎮 生动的游戏类比
想象英雄联盟中的回城机制:
- 按 B 开始回城,等待 8 秒
- 受到伤害立即打断,重新计时
- 必须完整等待才能回城成功
💻 实际应用案例
- 搜索建议功能
// 实现搜索框防抖
const searchInput = document.querySelector('#search');
const debouncedSearch = debounce(async (query) => {
const results = await fetchSearchResults(query);
updateSearchSuggestions(results);
}, 300);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
- 表单验证
// 实现实时表单验证
const emailInput = document.querySelector('#email');
const debouncedValidate = debounce(async (email) => {
const isValid = await validateEmail(email);
updateValidationUI(isValid);
}, 500);
emailInput.addEventListener('input', (e) => {
debouncedValidate(e.target.value);
});
⚡ 节流(Throttle)
🎮 游戏类比
类似英雄联盟技能冷却:
- 释放技能进入冷却时间
- 冷却期间无法再次释放
- 冷却结束才能再次使用
💻 实际应用案例
- 无限滚动加载
// 实现滚动加载
const container = document.querySelector('#infinite-list');
const throttledLoad = throttle(async () => {
if (isNearBottom()) {
const newItems = await fetchMoreItems();
appendItems(newItems);
}
}, 200);
window.addEventListener('scroll', throttledLoad);
- 数据统计上报
// 实现用户行为统计
const tracker = throttle((event) => {
sendAnalytics({
type: event.type,
timestamp: Date.now(),
data: event.data
});
}, 1000);
document.addEventListener('mousemove', tracker);
🔄 如何选择防抖还是节流?
选择防抖的场景 🛡️
- ✅ 搜索框输入查询
- ✅ 表单实时验证
- ✅ 调整窗口大小
- ✅ 用户输入校验
选择节流的场景 ⚡
- ✅ 页面滚动处理
- ✅ 数据统计上报
- ✅ 游戏中的按键处理
- ✅ 射击类游戏的武器发射
💡 性能优化建议
-
延迟时间设置 ⏱️
- 搜索框:300-500ms
- 表单验证:400-600ms
- 滚动处理:150-300ms
- 统计上报:1000-2000ms
-
代码优化 📈
- 使用闭包保存状态
- 注意内存泄漏
- 及时清除定时器
- 考虑是否需要立即执行
📝 总结
选择合适的方案:
- 防抖:关注最终结果
- 节流:关注执行频率
🔗 完整实现代码
防抖实现:
// debounce.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input style="width: 80%; height: 30px" type="text" id="id" />
<script>
let timer = null;
function debounce(func, delay, immediate = false) {
return (...args) => {
//清除定时器 如果注释的话还是那么多频次,只是每次推迟了
if (timer) {
clearTimeout(timer);
}
if (!timer && immediate) {
func(...args);
}
timer = setTimeout(() => {
func(...args);
}, delay);
};
}
function pureFn(...params) {
console.log('%c执行成功params:', 'color: red; font-size: 14px;', params);
}
const dom = document.getElementById('id');
const debounceFn = debounce(pureFn, 500, true);
dom.addEventListener('input', e => {
const value = e.target.value;
debounceFn(value, Date.now());
});
</script>
</body>
</html>
节流实现:
// throttle.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input style="width: 80%; height: 30px" type="text" id="id" />
<script>
const throttle = (func, delay) => {
let last = 0;
let deferTimer = null;
return args => {
// 干掉触发
let now = Date.now();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
func(args);
}, delay);
} else {
last = now; // 第一次时间
func(args); // 先执行一次
}
};
};
function pureFn(...params) {
console.log('%c执行成功params:', 'color: red; font-size: 14px;', params);
}
const dom = document.getElementById('id');
const throttleFn = throttle(pureFn, 1000, false);
dom.addEventListener('input', e => {
const value = e.target.value;
throttleFn(value, Date.now());
});
</script>
</body>
</html>
🌟 如果这篇文章对你有帮助,欢迎点赞、收藏和评论!
📢 关注我,一起探讨前端技术!