防抖和节流是什么?
防抖和节流本质上都是前端对过高频率执行的限制
。
防抖
: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
节流
: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
防抖是公交车,等最后一个人上来之后再多等30s,每次有人上来就重新计时。
节流是地铁,五分钟内就只有这一班。
一、防抖
也不要瞎使用防抖,使用防抖有3个条件:
- 频繁调用某个函数
- 造成效率问题
- 需要的结果以最后一次为准
应用场景:
- 输入框中频繁的输入内容,搜索或者提交信息
- 频繁的点击按钮,触发某个事件
- 监听浏览器滚动事件,完成某些特定操作
- 用户缩放浏览器的resize事件
1、封装一个防抖函数
先解决通用,再解决this指向,再解决参数问题
function debounce(func, delay = 500) {
let timerId;
return function (...args) {
// 因为this指向问题 这里写普通函数
clearTimeout(timerId)
timerId = setTimeout(() => {
func.apply(this, args) // 箭头函数没有this
clearTimeout(timerId) // 这两行不加也行
timerId= null
}, delay)
}
}
const debouncedFunction = debounce(myFunction, 500)
window.onresize = debouncedFunction()
2、vue2自定义防抖指令
局部注册
<template>
<input v-model="searchQuery" v-debounce="handleSearch" :debounce-delay="500" />
</template>
<script>
export default {
data() {
return {
searchQuery: ''
};
},
methods: {
handleSearch() {
console.log('Search query:', this.searchQuery)
},
},
directives: {
debounce: {
inserted: function (el, binding) {
let timer
el.addEventListener('input', function () {
clearTimeout(timer)
timer = setTimeout(function () {
binding.value()
}, binding.arg || 500)
})
}
}
}
}
</script>
全局注册
// directives/debounce.js
import Vue from 'vue'
Vue.directive('debounce', {
inserted: function (el, binding) {
let timer
el.addEventListener('input', function () {
clearTimeout(timer)
timer = setTimeout(function () {
binding.value()
}, binding.value || 500)
})
}
})
// main.js (或你的入口文件)
import Vue from 'vue'
import App from './App.vue'
import './directives/debounce'; // 导入自定义指令
new Vue({
render: (h) => h(App)
}).$mount('#app')
3、vue3自定义防抖指令
局部注册
<template>
<input v-model="searchQuery" v-debounce="handleSearch" :debounce-delay="500" />
</template>
<script>
import {
ref, defineComponent } from 'vue'
import debounceDirective from './directives/debounce' // 导入自定义指令
export default defineComponent({
directives: {
debounce: debounceDirective, // 局部注册自定义指令
},
setup() {
const searchQuery = ref('')
function handleSearch() {
// 这里放置搜索逻辑
console.log('搜索关键词:', searchQuery.value)
}
return {
searchQuery,
handleSearch,
}
}
})
</script>
// directives/debounce.js
export default {
mounted(el, binding) {
let timer
el.addEventListener('input', function () {
clearTimeout(timer)
timer = setTimeout(function () {
binding.value()
}, binding.value || 500)
})
}
}
全局注册
<template>
<input v-model="searchQuery" v-debounce="handleSearch" :debounce-delay="500" />
</template>
<script>
import {
ref } from 'vue'
export default {
setup() {
const searchQuery = ref('')
function handleSearch() {
// 这里放置搜索逻辑
console.log('搜索关键词:', searchQuery.value)
}
return {
searchQuery,
handleSearch,
}
}
}
</script>
// directives/debounce.js
export default {
mounted(el, binding) {
let timer
el.addEventListener('input', function () {
clearTimeout(timer)
timer = setTimeout(function () {
binding.value()
}, binding.value || 500)
})
}
}
// main.js (或你的入口文件)
import {
createApp } from 'vue'
import App from './App.vue'
import debounceDirective from './directives/debounce' // 导入自定义指令
const app = createApp(App)
// 全局注册自定义指令
app.directive('debounce', debounceDirective)
app.mount('#app')
//debounce.ts
import {
App, DirectiveBinding } from 'vue'
export default (app: App<Element>) => {
app.directive('debounce', {
mounted(el: HTMLElement, binding: DirectiveBinding) {
let timer: NodeJS.Timeout | null = null
el.addEventListener('click', () => {
const firstClick = !timer
if (firstClick) {
binding.value()
}
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
timer = null
if (!firstClick) {
binding.value()
}
}, 1000)
})
}
})
}
// main.ts
import debounce from '@/directives/debounce'
app.use(debounce)
v-debounce="search"
例子:
<template>
<div>
<input v-model="inputValue" @keydown.enter="handleInputEnter" />
</div>
</template>
import {
ref } from 'vue';
export default {
setup() {
const inputValue = ref('')
// 封装防抖函数
const debounce = (fn, delay) => {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
// 处理输入事件的防抖函数
const handleInputEnterDebounced = debounce(() => {
// 在这里处理输入内容的逻辑
console.log('Enter key pressed!')
console.log('Input value:', inputValue.value);
}, 500) // 设置防抖延迟时间为500毫秒
// 输入事件处理函数
const handleInputEnter = () => {
handleInputEnterDebounced() // 使用防抖函数处理输入事件
};
return {
inputValue,
handleInputEnter
}
}
}
二、节流
应用场景:
- 监听页面的滚动事件
- 鼠标移动事件
- 用户频繁点击按钮操作
- 游戏中的一些设计
1、封装一个节流函数
var throttle = function(func, delay = 500) {
let timer = null
return function(...args) {
if (!timer) {
timer = setTimeout(function() {
func.apply(this, args)
clearTimeout(timer)
timer = null
}, delay)
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000))
2、vue2自定义节流指令
局部注册
<template>
<input v-model="searchQuery" v-throttle="handleSearch" :throttle-delay="500" />
</template>
<script>
export default {
data() {
return {
searchQuery: ''
};
},
directives: {
throttle: {
inserted: function (el, binding) {
let timer = null
el.addEventListener('input', function () {
if (!timer) {
timer = setTimeout(function () {
binding.value()
timer = null
}, binding.arg || 500)
}
})
}
}
},
methods: {
handleSearch() {
// 这里放置搜索逻辑
console.log('搜索关键词:', this.searchQuery)
}
}
}
</script>
全局注册
import Vue from 'vue'
import App from './App.vue'
// 自定义节流指令
Vue.directive('throttle', {
inserted: function (el, binding) {
let timer = null
el.addEventListener('input', function () {
if (!timer) {
timer = setTimeout(function () {
binding.value()
timer = null
}, binding.arg || 500)
}
})
}
})
new Vue({
render: (h) => h(App)
}).$mount('#app')
// directive/throttle.js
export default {
inserted: function (el, binding) {
let timer = null
el.addEventListener('input', function () {
if (!timer) {
timer = setTimeout(function () {
binding.value()
timer = null
}, binding.arg || 500)
}
})
}
}
// main.js (或你的入口文件)
import Vue from 'vue'
import App from './App.vue'
import throttleDirective from './directive/throttle'
// 自定义节流指令全局注册
Vue.directive('throttle', throttleDirective)
new Vue({
render: (h) => h(App)
}).$mount('#app')
3、vue3自定义节流指令
// main.js (或你的入口文件)
import {
createApp } from 'vue';
import App from './App.vue';
import throttleDirective from './directives/throttle';
const app = createApp(App);
// 自定义节流指令全局注册
app.directive('throttle', throttleDirective);
app.mount('#app')
// directives/throttle.js
export default {
beforeMount(el, binding) {
let timer = null
el.addEventListener('input', () => {
if (!timer) {
timer = setTimeout(() => {
binding.value()
timer = null
}, binding.arg || 500)
}
})
}
}
// throttle.ts
import {
App, DirectiveBinding } from 'vue'
export default (app: App<Element>) => {
app.directive('throttle', {
mounted(el: HTMLElement, binding: DirectiveBinding) {
let timer: NodeJS.Timeout | null = null
el.addEventListener('click', () => {
if (!timer) {
timer = setTimeout(() => {
binding.value()
timer = null
}, 5000)
}
})
}
})
}
例子:
<template>
<!-- <RouterView /> -->
<div class="aaa" @mousemove="move"></div>
</template>
<script setup lang="ts">
// import { RouterView } from 'vue-router'
const throttle = (fn, delay = 500) => {
let timeId = null
return function(...args) {
if (!timeId) {
timeId = setTimeout(function() {
fn.apply(this, args)
clearTimeout(timeId)
timeId = null
}, delay)
}
}
}
const move = throttle((e) => {
console.log('Mouse coordinates:', e.clientX, e.clientY)
}, 1000)
</script>
<style lang="less">
.aaa{
width: 800px;
height: 800px;
background-color: pink;
z-index: 999;
}
</style>