防抖
防抖的适用场景
常见的前端性能优化方案 , 它可以防止JS高频渲染页面时出现的视觉抖动(卡顿)
比如:
- 示例1: 页面改变尺寸时,同步调整图表的大小
- 示例2: 输入内容时,结合ajax进行搜索并渲染结果
如果内容的渲染速度过快,都可能会造成抖动效果,并且连带会浪费性能
- 频繁执行逻辑代码,耗费浏览器性能
- 频繁发送请求去服务器,耗费服务器性能
适用场景:
-
在触发频率高的事件中
- 频率高的事件: resize、input 、scroll 、keyup….
-
执行耗费性能操作
- 耗费性能的操作: 操纵页面、网络请求….
-
需要实现的效果: 连续操作之后只有最后一次生效
这个时候就可以适用防抖来进行优化
手写防抖
防抖优化之后的效果可以通过一些具体的网站来进行确认,比如12306,他就是通过防抖进行的优化:
- 在输入内容的时候没有发送请求
- 输入完毕之后,稍等一小会才发送请求去服务器
这个就是防抖的效果: 连续事件停止触发后,一段时间内没有再次触发,就执行业务代码
核心实现步骤
- 开启定时器,保存定时器id
- 清除已开启的定时器
那个输入框+搜索的例子优化之后代码如下:
let timeId
document.querySelector('.search-city').addEventListener('input', function () {
// 2. 清除已开启的定时器
clearTimeout(timeId)
// 1. 开启定时器,保存定时器id
timeId = setTimeout(() => {
renderCity(this.value)
}, 500)
})
lodash的debounce方法
实际开发中一般不需要手写防抖,因为已经有库里面提供了对应的方法,可以直接调用,也可以自己手写实现debounce:
lodash工具库中的debounce方法(常用)debounce的实现原理(高频面试题)
_.debounce(func, [wait=0], [options=])
参数
func(Function) : 要防抖动的函数。[wait=0](number) : 需要延迟的毫秒数。[options=](Object) : 选项对象。[options.leading=false](boolean) : 指定在延迟开始前调用。[options.maxWait](number) : 设置func允许被延迟的最大值。[options.trailing=true](boolean) : 指定在延迟结束后调用。
返回
(Function) : 返回新的 debounced(防抖动)函数。
注意:
- 实际开发时一般给前2个参数即可,然后适用返回的函数替换原函数即可
- 项目中如果有
lodash那么直接使用它提供的debounce即可,不仅可以实现防抖,原函数中的this和参数均可以正常使用
手写debounce函数
手写实现debounce函数,实现lodash中debounce方法的核心功能
需求:
-
参数:
func(Function) : 要防抖动的函数。[wait=0](number) : 需要延迟的毫秒数。
-
返回值:
- (Function) : 返回新的 debounced(防抖动)函数。
核心步骤:
- 返回防抖动的新函数
- 原函数中的
this可以正常使用 - 原函数中的参数可以正常使用
function debounce(func, wait = 0) {
let timeId
// 防抖动的新函数
return function (...args) {
let _this = this
clearTimeout(timeId)
timeId = setTimeout(function () {
// 通过apply调用原函数,并指定this和参数
func.apply(_this, args)
}, wait)
}
}
节流
节流的适用场景
常见的前端性能优化方案 , 它可以防止高频触发事件造成的性能浪费:比如
- 播放视频时同步缓存播放时间
- 如果要多设备同步,还需要通过
ajax提交到服务器
高频触发耗费性能的操作,会造成性能浪费
适用场景* *:**在触发频率高的事件中, 执行耗费性能操作 , 连续触发 , 单位时间内只有一次生效
优化之前: 每当触发事件就会执行业务逻辑
优化之后: 触发事件之后延迟执行逻辑,在逻辑执行完毕之后无法再次触发
手写节流
核心步骤:
1.开启定时器,并保存 id
2.判断是否已开启定时器
3.定时器执行时 , id设置为空
// 播放器案例优化之后代码
let timeId
video.addEventListener('timeupdate', function () {
if (timeId !== undefined) {
return
}
timeId = setTimeout(() => {
console.log('timeupdate触发')
localStorage.setItem('currentTime', this.currentTime)
timeId = undefined
}, 3000)
})
lodash的throttle方法
实际开发中一般不需要手写节流,因为已经有库里面提供了对应的方法,可以直接调用,也可以自己手写实现throttle,一会咱们就从这两个方面进行讲解:
lodash工具库中的throttle方法(常用)throttle的实现原理(高频面试题)
_.throttle(func, [wait=0], [options=])
参数
func(Function) : 要节流的函数。[wait=0](number) : 需要节流的毫秒。[options=](Object) : 选项对象。[options.leading=true](boolean) : 指定调用在节流开始前。[options.trailing=true](boolean) : 指定调用在节流结束后。
返回
(Function) : 返回节流的函数。
注意:
-
实际开发时一般会给3个参数即可,然后适用返回的函数替换原函数即可
- 参数3:
options.leading=true默认为true,开始时触发节流函数,一般设置为false
- 参数3:
-
项目中如果有
lodash那么直接使用它提供的throttle即可,不仅可以实现节流,原函数中的this和参数均可以正常使用
// 播放器案例使用`lodash` 优化之后的结果如下
const func = function (e) {
console.log('timeupdate触发')
console.log('e:', e)
localStorage.setItem('currentTime', this.currentTime)
}
const throttleFn = _.throttle(func, 1000, { leading: false })
video.addEventListener('timeupdate', throttleFn)
手写throttle方法
手写实现throttle函数,实现lodash中throttle方法的核心功能
需求:
-
参数:
func(Function) : 要节流的函数。[wait=0](number) : 需要节流的毫秒。
-
返回值:
- (Function) : 返回节流的函数
核心步骤:
- 返回节流的新函数
- 原函数中的
this可以正常使用 - 原函数中的参数可以正常使用
// 节流工具函数
function throttle(func, wait = 0) {
let timeId
return function (...args) {
if (timeId !== undefined) {
return
}
const _this = this
timeId = setTimeout(() => {
func.apply(_this, args)
timeId = undefined
}, wait)
}
}
总结
- 节流:触发事件之后延迟执行逻辑,在逻辑执行完毕之后无法再次触发(类似于 lol 中的“技能冷却”)
- 防抖:连续操作之后只有最后一次生效(类似于 lol 中的“回城”)