# 场景

# 节流的常见场景

在日常开发中,我们可能会遇见这么一种场景,一个元素的宽高需要跟随窗口的变化来变化;例如:当我们使用 element-UI 的表格组件时,表格的数据是不确定的,但我们需要固定表头,当内容过多时可以对内容进行滚动,这时我们就需要给组件提供一个具体的高度,但是浏览器的窗口宽高是会变化的(人为的去缩放浏览器),这就要求我们去监听窗口的变化,当窗口宽高变化的时候,执行一个函数,重新计算浏览器宽高,让表格的高度发生变化

# 防抖的常见场景

日常开发过程中,我们可能会遇到实时搜索的场景;例如:用户在搜索框输入了内容,我们要根据内容实时的给用户推荐相关的搜索语,让用户选择。

# 问题

在上述两个场景中,有一个共同点就是函数被高频的调用,无论是实时窗口变化还是输入框的实时变化,都会超高频的触发 resizeinput 事件,但不同的是,在节流的场景中,我们更希望的是在一定的时间段,计算窗口宽高的函数只被执行一次;在防抖的场景中,我们更希望用户输入结束后,再根据输入内容,给用户推荐搜索语。

# 解决方法

# 节流的代码解决

对于节流的场景,我们的目标是在一定的时间段内,函数只被执行一次;我们假设在窗口变化过程中,每200毫秒计算一次浏览器宽高,也就是每200毫秒修改一次元素大小


const getWindowHeight = () => {
  const {width,height} = window.screen;
  console.log('浏览器宽度为:',width, '浏览器高度为:', height);
}
// 计算窗口宽高的变化
const throttle = (fn) => {
  let timeNo;
  return () => {
  	console.log('窗口变化');
    if(timeNo) return;
    timeNo = setTimeout(() => {
      fn()
      clearTimeout(timeNo);
      timeNo = null;
    }, 200);
  }
}
// 监听窗口宽高变化
window.addEventListener('resize', throttle(getWindowHeight))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 防抖的代码解决

对于防抖的场景,我们的目标是,在高频被触发时,以最后一次触发,去执行函数;我们假定时间间隔是200毫秒,也就是说用户在搜索框输入内容触发 input 事件后,等200毫秒后再去拿输入框内容,在这200毫秒内用户输入了,我们就重新计时等待;

const input = document.querySelector('#input'); // 拿输入框对象
const debounce = () => {
  let timeNo;
  return (e) => {
    clearTimeout(timeNo);
    timeNo = setTimeout(() => {
      const {target: {value}} = e;
      console.log('输入框的值为',value)
      clearTimeout(timeNo);
    },200)
  }
}
input.addEventListener('input', debounce()); // 监听输入框的input事件
1
2
3
4
5
6
7
8
9
10
11
12
13

# 概念

函数节流 即在一个事件被连续的触发过程中,以一个固定的时间长度,间隔的去执行一个函数;

函数防抖 即在一个事件被连续的触发过程中,只在最后一次触发时执行一个函数

# 优化

# 对函数节流的通用封装

/**
 * 通用版节流函数
 *
 * @param {function} fn - 要被执行的方法, 相隔多长时间要被执行的方法
 * @param {number} Intervals - 间隔时间, 相隔多长时间调用一次对应方法
 * @params {any} args - 剩余参数,剩余参数将会在调用fn时作为参数传给fn
 * @params {any} params - 以下方使用例子看,resize事件被触发的时候,会在传个event对象过去,所以同样需要接收
 * @return function
 */
const throttle = (fn, Intervals, ...args) => {
  let timeNo;
  return (...params) => {
    if(timeNo) return;
    timeNo = setTimeout(() => {
      fn(...args,...params)
      clearTimeout(timeNo);
      timeNo = null;
    }, Intervals);
  }
}
// 使用例子
const obj = {
  name: '张三',
  fun(params, str) {
    console.log(this.name, '接收参数', params, str);
  }
}
window.addEventListener('resize', throttle(obj.fun.bind(this), 200, {text: '最外面传的第一个参数'}, '最外面传的第二个参数'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 对函数防抖的通用封装

/**
 * 通用版防抖函数
 *
 * @param {function} fn - 要被执行的方法, 相隔多长时间要被执行的方法
 * @param {number} Intervals - 间隔时间, 相隔多长时间调用一次对应方法
 * @params {any} args - 剩余参数,剩余参数将会在调用fn时作为参数传给fn
 * @params {any} params - 以下方使用例子看,input事件被触发的时候,会在传个event对象过去,所以同样需要接收
 * @return function
 */
const debounce = (fn, Intervals, ...args) => {
  let timeNo;
  return (...params) => {
    clearTimeout(timeNo);
    timeNo = setTimeout(() => {
      fn(...args,...params)
      clearTimeout(timeNo);
    }, Intervals)
  }
}
// 使用例子
const getInputValue = (e) => {
  const {target: {value}} = e;
  console.log('输入框的值为',value)
}
const input = document.querySelector('#input'); // 拿输入框对象
input.addEventListener('input', debounce(getInputValue,200)); // 监听输入框的input事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Last Updated: 2020-4-27 22:56:04