防抖(debounce)
含义
当某函数被高频持续触发时,只在一段时间内无触发,函数才会执行。如按钮点击更新,多次点击按钮会触发对应的更新函数,函数在高频点击过程中不会执行,只有最后一次点击,且规定的时间段内未再次点击时,更新函数触发,执行更新操作。
应用场景:
- 搜索框搜索输入,用户最后一次输入完,再发送请求
- 手机号,邮箱验证输入检测
- 窗口大小resize。窗口调整完成后计算窗口大小,防止重复渲染。
代码
处理方式:利用定时器,设置延迟时间,延迟时间内未再次触发,才执行函数,保证在单位时间内在最后的时刻触发。
function debounce(func, wait) {
let timeout;
return function () {
let context = this; //保存this指向
let args = arguments;
if (timeout) clearTimeout(timeout)
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
}
注:用let context = this;保存this指向的原因是,在 setTimeout 中的回调函数的 this 默认指向全局对象 window,如果是严格模式下则为 undefined。
如果setTimeout中是箭头函数,而箭头函数与普通函数不同,它们不会创建自己的 this,而是捕获词法环境中的 this 值,也就是说,它们继承了定义它们的位置的 this 值。
箭头函数被定义在返回的匿名函数内部,因此它继承了该匿名函数的 this,而该匿名函数的 this 由调用它的上下文决定。故上述代码等价于:
function debounce(func, wait) {
let timeout;
return function () {
let args = arguments;
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(this, args)
}, wait);
}
}
节流(throttle)
含义
一段时间内函数持续,频繁触发。在单位时间内只执行一次函数。节流会稀释函数的执行频率。
应用场景:
- 高频事件 例如:快速点击、鼠标滑动、resize,scroll事件(每触发或者滚动1像素就执行),拖拽时候的mousemove。
- 滚动加载,加载更多或滚到底部监听
- 搜索框,搜索联想功能
代码
处理方式:利用定时器,等定时器执行完毕,才开启定时器,保证单位时间内执行一次。重点在于单位时间内能执行一次触发,保证触发一次即可稀释函数的执行频率。
function throttle(fn, delay = 500) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)timer = null
}, delay);
}
}
}
vue项目中应用
使用节流防抖函数
应用场景:搜索输入优化
在实时搜索(如搜索框自动完成功能)中,我们希望只在用户停止输入后才发起搜索请求,以减少不必要的服务器负载和网络请求。
<label for="search-input">Name:</label>
<input type="text" id="search-input" name="name">
<script>
function debounce(func, delay) {
let timeout;
return function (...args) {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}

function handleSearchInput(event) {
console.log("Fetching data for:", event.target.value);
// 实际的搜索请求逻辑
}
const debouncedHandleSearchInput = debounce(handleSearchInput, 1000);
document.getElementById('search-input').addEventListener('input', debouncedHandleSearchInput);
</script>
应用场景:窗口调整大小事件优化
在调整浏览器窗口大小时,resize 事件可以非常频繁地触发,这可能导致性能问题。使用节流可以限制这个函数的执行频率。
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
function handleResize() {
console.log('Window size:', window.innerWidth, 'x', window.innerHeight);
}
const throttledHandleResize = throttle(handleResize, 200);
window.addEventListener('resize', throttledHandleResize);
使用lodash库函数
还可以直接使用lodash的库函数实现节流防抖
在实现搜索功能时,通常希望在用户停止输入一段时间后再进行查询,以减少不必要的服务器请求。以下是一个使用 lodash 的 debounce 方法来实现这一功能的示例:
import _ from 'lodash';
// 假设这是一个搜索函数,它会根据用户的输入进行数据查询
function search(query) {
console.log('Searching for:', query);
// 实际的查询逻辑可以是发送一个API请求到服务器
}
// 使用lodash的debounce方法来包装search函数
const debouncedSearch = _.debounce(search, 300); // 300毫秒后执行
// 假设这是一个输入框元素的事件处理器
document.getElementById('search-input').addEventListener('input', (event) => {
debouncedSearch(event.target.value);
});
在处理窗口大小调整(resize)事件时,可以使用 throttle 函数来限制事件处理函数的调用频率,防止因频繁处理导致性能问题。以下是使用 lodash 的 throttle 方法来实现这一功能的示例:
import _ from 'lodash';
// 窗口调整大小时要执行的函数
function handleResize() {
const width = window.innerWidth;
const height = window.innerHeight;
console.log('Window size:', width, 'x', height);
}
// 使用lodash的throttle方法来包装handleResize函数
const throttledHandleResize = _.throttle(handleResize, 200); // 每200毫秒最多执行一次
// 添加窗口大小调整的事件监听器
window.addEventListener('resize', throttledHandleResize);
全局自定义指令实现节流防抖
- 定义节流函数:创建一个节流函数,该函数确保在指定的时间间隔内最多执行一次函数。
- 创建自定义指令:使用 Vue 的
directive方法来注册全局自定义指令。
示例:
// 定义一个节流函数
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
}
}
// 注册一个全局自定义指令 `v-throttle` --vue2版本
Vue.directive('throttle', {
inserted: function(el, binding) {
let throttleFunction = throttle(binding.value, binding.arg || 2000); // 默认 2000 毫秒
el.addEventListener('click', throttleFunction);
},
unbind: function(el, binding) {
el.removeEventListener('click', binding.value);
}
});
// 注册一个全局自定义指令 `v-throttle` --vue3版本
Vue.directive('throttle', {
mounted: function(el, binding) {
let throttleFunction = throttle(binding.value, binding.arg || 2000); // 默认 2000 毫秒
el.addEventListener('click', throttleFunction);
},
unmounted: function(el, binding) {
el.removeEventListener('click', binding.value);
}
});
// 定义一个防抖函数
function debounce(func, delay) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, delay);
}
}
// 注册一个全局自定义指令 `v-debounce` --vue2版本
Vue.directive('debounce', {
inserted: function(el, binding) {
let debounceFunction = debounce(binding.value, binding.arg || 300); // 默认 300 毫秒
el.addEventListener('keyup', debounceFunction);
},
unbind: function(el, binding) {
el.removeEventListener('keyup', binding.value);
}
});
// 注册一个全局自定义指令 `v-debounce` --vue3版本
Vue.directive('debounce', {
mounted: function(el, binding) {
let debounceFunction = debounce(binding.value, binding.arg || 300); // 默认 300 毫秒
el.addEventListener('keyup', debounceFunction);
},
unmounted: function(el, binding) {
el.removeEventListener('keyup', binding.value);
}
});
注意事项: 全局自定义指令在vue2和vue3中不同,在 Vue 3 中,自定义指令的生命周期钩子确实发生了一些变化。以下是详细的解释:
1. 指令的生命周期钩子
Vue 2 vs Vue 3
- Vue 2 中,指令的生命周期钩子主要包括
bind、inserted、update、componentUpdated和unbind。- Vue 3 中,这些钩子被简化和重命名为
created、beforeMount、mounted、beforeUpdate、updated、beforeUnmount和unmounted。2. 绑定和解绑事件监听器
在 Vue 3 中,我们通常使用
mounted和unmounted钩子来绑定和解绑事件监听器。这样做的目的是确保当元素被插入或移除 DOM 时,我们能够正确地添加或移除事件监听器。示例解释
- 绑定事件监听器(mounted) : 当元素被插入 DOM 时(在
mounted钩子中),我们绑定事件监听器。这样可以确保事件监听器在元素实际存在于页面上时才会绑定。- 解绑事件监听器(unmounted) : 当元素从 DOM 中移除时(在
unmounted钩子中),我们解绑事件监听器。这是为了防止内存泄漏,因为如果不解绑,事件监听器会继续存在,即使元素已经从页面上移除。
使用指令
现在,您可以在任何 Vue 组件的模板中使用 v-throttle 和 v-debounce:
<!-- 使用节流指令 -->
<button v-throttle:3000="myMethod">Click me</button>
<!-- 使用防抖指令 -->
<input v-debounce:300="myInputMethod">