防抖/节流基本概念用法及概念介绍&&vue防抖节流指令封装

265 阅读3分钟

防抖/节流概念介绍及基本用法

防抖(debounce)

  • 概念触发事件后,函数在n秒内只执行一次,如果事件再次触发,则重置事件触发事件,也就是说只要我一直不停的触发一个事件,那么这个事件的执行时机会一直往后推迟,直到我停止触发事件时才去触发事件对应的回调
  • 实际应用input输入框频繁输入,进行模糊查找。在最后一次键盘抬起后的一秒,进行数据请求,应用防抖,防止用户每次输入都调用接口浪费资源
  • js实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
   <input type="text" id="debounceElement">
   
   <script>
    let el = document.querySelector("#debounceElement")
    const debounce = (fn,delay) => {
      let timer
      return (el => {  //return的这整个函数,所以addEventListener的参数就是这地方的参数
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          fn(el)
        }, delay)
      }
    }
    let debounceFn = (el) =>{
      console.log('触发防抖事件',el);
    }
    el.addEventListener('keyup',debounce(debounceFn,300))
</script>
</body>
</html>

节流(throttle)

  • 概念规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
  • 实际应用window的onresize事件,监听resize的时候,我们的回调会触发的相当频繁浪费性能,但是其实也许我们只需要让他有规律的触发就好了?比如300ms触发一次
  • js实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    const throttle = (fn,delay) => {
      var prev = Date.now()
      return (el) => {  //return的这整个函数,所以addEventListener的参数就是这地方的参数
        console.log(3333);
        var now = Date.now()
        if (now - prev > delay) {
          fn(el)                
          prev = Date.now()             
        }    
      }
    }
    let throttleFn = (el) =>{
      console.log('节流事件',el);
    }
    // console.log();
    window.addEventListener('resize',throttle(throttleFn,5000))
  </script>
</body>
</html>

Vue封装防抖/节流指令

先来了解一下指令

简介

默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

指令注册

  • 全局注册
// 注册一个全局自定义指令 `v-focus` 
Vue.directive('focus', { 
// 当被绑定的元素插入到 DOM 中时…… 
    inserted: function (el) { // 聚焦元素 el.focus() }
})
  • 局部注册
    directives: { 
        focus: { // 指令的定义 inserted: 
            function (el) {
                el.focus() 
            } 
        } 
    }

钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。

  • binding:一个对象,包含以下 property:

    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

基于指令封装防抖节流的实现

防抖封装

const debounce = ()=>{
  const debounceFn = (fn,delay) => {
    let timer
    return () => {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        fn()
      }, delay)
    }
  }
  Vue.directive('debounce',{
      bind(el, binding) {
        let emit = binding.arg   //监听的事件
        let fn = binding.value   //触发的回调
        el.addEventListener(emit, debounceFn(fn, 300))
      },
      unbind(el,binding) {
        let emit = binding.arg
        el.removeEventListener(emit, debounceFn)
      },
  })
}

节流封装

const throttle = ()=>{
  const throttleFn = (fn,delay) => {
    var prev = Date.now()
    return () => {
      var now = Date.now()
      if (now - prev > delay) {
        fn()                
        prev = Date.now()             
      }    
    }
  }
  Vue.directive('throttle',{
      bind(el, binding) {
        let emit = binding.arg   //监听的事件
        let fn = binding.value   //触发的回调
        el.addEventListener(emit, throttleFn(fn, 500))
      },
      unbind(el,binding) {
        let emit = binding.arg
        el.removeEventListener(emit, throttleFn)
      },
  })
}

用法

v-debounce:input="getCollage"