再说防抖与节流

防抖和节流是什么?

防抖和节流本质上都是前端对过高频率执行的限制
防抖 : 在事件被触发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>

猜你喜欢

转载自blog.csdn.net/weixin_44582045/article/details/131829040