简介
节流和防抖是前端面试中老生常谈的话题,能大大提升程序的性能。但是节流防抖具体是什么?什么情况下使用呢?以及在Vue中应该怎么使用呢?下面请跟随笔者的脚步给大家逐步介绍。
防抖(debounce)
所谓防抖,就是指单位时间内函数只执行一次,如果在单位时间内重复触发该事件,则会重新计算函数执行时间。
常见的使用场景是在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
防抖又分为两种,分为立即执行版本和非立即执行版本。
立即执行版本
立即执行版本我们一般可以用在按钮点击上(比如提交表单),点击立即触发,单位时间内一直点击不会触发。当超过单位时间再次点击会再次触发。
function debounce1(func, delay) {
let timer = null
return function() {
const context = this
const args = arguments
if(timer) clearTimeout(timer)
const canCall = !timer
timer = setTimeout(() => {
timer = null
}, delay)
// if(canCall) func.apply(context, args)
if(canCall) func.call(context, ...args)
}
}
非立即执行版本
非立即执行版本我们可以应用在搜索联想功能中,输入内容的时候不触发,输入完后再请求后端获取数据。
function debounce2(func, delay) {
let timer = null
return function() {
const context = this
const args = arguments
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
//func.apply(context, args)
func.call(context, ...args)
}, delay)
}
}
组合版本
function debounce3(func, delay, immediate=true) {
let timer = null
return function() {
const context = this
const args = arguments
if(timer) clearTimeout(timer)
if(immediate) {
const canCall = !timer
timer = setTimeout(() => {
timer = null
}, delay)
// if(canCall) func.apply(context, args)
if(canCall) func.call(context, ...args)
} else {
timer = setTimeout(() => {
//func.apply(context, args)
func.call(context, ...args)
}, delay)
}
}
}
节流(throttle)
所谓节流,就是单位时间内不管触发多少次函数,只固定执行一次函数。 节流会降低函数的执行频率。
常见的使用场景是在一些 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
节流有定时器版本和时间戳版本。
定时器版本
function throttle1(func, delay) {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (!timer) {
timer = setTimeout(() => {
timer = null;
// func.apply(context, args);
func.call(context, ...args);
}, delay);
}
};
}
时间戳版本
function throttle2(func, delay) {
let pre = 0;
return function () {
const context = this;
const args = arguments;
let now = Date.now();
if (now - pre > delay) {
pre = now;
// func.apply(context, args);
func.call(context, ...args);
}
};
}
组合版本
function throttle3(func, delay, timestamp=true) {
let timer = null
let pre = 0
return function() {
const context = this
const args = arguments
if(timestamp) {
let now = Date.now()
if(now - pre > delay) {
pre = now
//func.apply(context, args)
func.call(context, ...args)
}
} else {
if(!timer) {
timer = setTimeout(() => {
timer = null;
// func.apply(context, args);
func.call(context, ...args);
}, delay)
}
}
}
}
在Vue中使用
很多小伙伴虽然知道防抖和节流,但是却不知道在Vue中如何使用节流和防抖,下面笔者介绍两种在Vue中使用防抖和节流的方法。
通过外部引入的方式
防抖
import { debounce } from "@/utils/index";
export default {
mounted() {
window.addEventListener("scroll", this.handleScroll);
},
methods: {
handleScroll: debounce(function (e) {
console.log(e);
}, 1000),
},
};
节流
import { throttle } from "@/utils/index";
export default {
mounted() {
window.addEventListener("scroll", this.handleScroll);
},
methods: {
handleScroll: throttle(function (e) {
console.log(e);
}, 1000),
},
};
写在本文件里面
防抖
mounted() {
window.addEventListener("scroll", this.debounce(this.handleScroll, 1000));
},
methods: {
debounce(func, delay) {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
},
handleScroll(e) {
console.log(e);
},
},
节流
mounted() {
window.addEventListener("scroll", this.throttle(this.handleScroll, 1000));
},
methods: {
throttle(func, delay) {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
},
handleScroll(e) {
console.log(e);
},
},
在React中使用
由于React较为灵活,所以使用方式和我们在js中使用是类似的。
通过外部引入的方式
React类组件
import { debounce } from "@/utils/index";
class Test extends Component {
handleScroll = () => {
console.log("scroll");
};
componentDidMount() {
window.addEventListener("scroll", debounce(this.handleScroll, 1000));
}
}
如果需要在渲染函数中使用需要绑定this,如果想不绑定this也可以,回调函数需要使用箭头函数。
import { debounce } from "@/utils/index";
class Test extends Component {
handleClick = debounce(function () {
console.log("click");
}, 2000);
// 回调函数需要使用箭头函数,可以不用绑定this
handleClick2 = debounce(() => {
console.log("click2");
}, 2000);
render() {
return <div>
<button onClick={this.handleClick.bind(this)}>
防抖,单位时间内重复触发重复计时
</button>
<button onClick={this.handleClick2}>
防抖,单位时间内重复触发重复计时
</button>
</div>
}
}
React函数组件
函数组件因为没有this,所以我们不需要考虑绑定this问题,所以普通函数和箭头函数作为回调都可以。
import { debounce } from "@/utils/index";
function Test() {
const handleScroll = () => {
console.log("scroll");
};
useEffect(() => {
window.addEventListener("scroll", debounce(handleScroll, 1000));
}, []);
}
如果需要在渲染函数中使用也是一样,包裹即可。
import { debounce } from "@/utils/index";
function Test() {
handleClick = debounce(function () {
console.log("click");
}, 2000);
return <button onClick={handleClick}>
防抖,单位时间内重复触发重复计时
</button>
}
写在本文件里面
React类组件
class Test extends Component {
handleScroll = () => {
console.log("scroll");
};
debounce = (func, delay) => {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
const canCall = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canCall) func.call(context, ...args);
};
};
componentDidMount() {
window.addEventListener("scroll", this.debounce(this.handleScroll, 1000));
}
}
如果需要在渲染函数中使用需要绑定this,如果想不绑定this也可以,回调函数需要使用箭头函数。
class Test extends Component {
debounce = (func, delay) => {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
const canCall = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canCall) func.call(context, ...args);
};
};
handleClick = this.debounce(function () {
console.log("click");
}, 2000);
// 回调函数需要使用箭头函数,可以不用绑定this
handleClick2 = this.debounce(() => {
console.log("click2");
}, 2000);
render() {
return <div>
<button onClick={this.handleClick.bind(this)}>
防抖,单位时间内重复触发重复计时
</button>
<button onClick={this.handleClick2}>
防抖,单位时间内重复触发重复计时
</button>
</div>
}
}
React函数组件
函数组件因为没有this,所以我们不需要考虑绑定this问题,所以普通函数和箭头函数作为回调都可以。
function Test() {
const debounce = (func, delay) => {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
const canCall = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canCall) func.call(context, ...args);
};
};
const handleScroll = () => {
console.log("scroll");
};
useEffect(() => {
window.addEventListener("scroll", debounce(handleScroll, 1000));
}, []);
}
如果需要在渲染函数中使用也是一样,包裹即可。
import { debounce } from "@/utils/index";
function Test() {
const debounce = (func, delay) => {
let timer = null;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
const canCall = !timer;
timer = setTimeout(() => {
timer = null;
}, delay);
if (canCall) func.call(context, ...args);
};
};
handleClick = debounce(function () {
console.log("click");
}, 2000);
return <button onClick={handleClick}>
防抖,单位时间内重复触发重复计时
</button>
}
后记
本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!