React中使用防抖函數(shù)和節(jié)流函數(shù)
在React事件調(diào)用時(shí),React傳遞給事件處理程序是一個(gè)合成事件對象的實(shí)例。SyntheticEvent對象是通過合并得到的。 這意味著在事件回調(diào)被調(diào)用后,SyntheticEvent 對象將被重用并且所有屬性都將被取消。 這是出于性能原因。 因此,您無法以異步方式訪問該事件。React合成事件官方文檔
所以在用防抖或節(jié)流函數(shù)封裝時(shí),異步方式訪問事件對象出現(xiàn)問題。解決的方法如下:
方法一:調(diào)用合成事件對象的persist()方法 event.persist && event.persist() //保留對事件的引用
方法二:深拷貝事件對象 const event = e && {...e} //深拷貝事件對象
function debounce(func, wait=500) {let timeout; // 定時(shí)器變量return function(event){clearTimeout(timeout); // 每次觸發(fā)時(shí)先清除上一次的定時(shí)器,然后重新計(jì)時(shí)event.persist && event.persist() //保留對事件的引用//const event = e && {...e} //深拷貝事件對象timeout = setTimeout(()=>{func(event)}, wait); // 指定 xx ms 后觸發(fā)真正想進(jìn)行的操作 handler};}防抖debounce
防抖 Debounce 多次觸發(fā),只在最后一次觸發(fā)時(shí),執(zhí)行目標(biāo)函數(shù)。
函數(shù)防抖就是,延遲一段時(shí)間再執(zhí)行函數(shù),如果這段時(shí)間內(nèi)又觸發(fā)了該函數(shù),則延遲重新計(jì)算。
應(yīng)用場景
(1)通過監(jiān)聽某些事件完成對應(yīng)的需求,比如:
通過監(jiān)聽 scroll 事件,檢測滾動位置,根據(jù)滾動位置顯示返回頂部按鈕
通過監(jiān)聽 resize 事件,對某些自適應(yīng)頁面調(diào)整DOM的渲染(通過CSS實(shí)現(xiàn)的自適應(yīng)不再此范圍內(nèi))
通過監(jiān)聽 keyup 事件,監(jiān)聽文字輸入并調(diào)用接口進(jìn)行模糊匹配
(2)其他場景
表單組件輸入內(nèi)容驗(yàn)證
防止多次點(diǎn)擊導(dǎo)致表單多次提交
簡單實(shí)現(xiàn)
function debounce(fn, wait) {let treturn () => {let context = thislet args = argumentsif (t) clearTimeout(t)t= setTimeout(() => {fn.apply(context, args)}, wait)}}完整實(shí)現(xiàn)
function debounce(func, wait, immediate) {let time;let debounced = function() {let context = this;if(time) clearTimeout(time);if(immediate) {let callNow = !time;if(callNow) func.apply(context, arguments);time = setTimeout(()=>{time = null} //見注解, wait)} else {time = setTimeout(()=>{func.apply(context, arguments)}, wait) }};debounced.cancel = function() {clearTimeout(time);time = null};return debounced}// underscore.js debounce//// Returns a function, that, as long as it continues to be invoked, will not// be triggered. The function will be called after it stops being called for// N milliseconds. If `immediate` is passed, trigger the function on the// leading edge, instead of the trailing._.debounce = function(func, wait, immediate) {var timeout, args, context, timestamp, result;// 處理時(shí)間var later = function() {var last = _.now() - timestamp;if (last < wait && last >= 0) {timeout = setTimeout(later, wait - last); // 10ms 6ms 4ms} else {timeout = null;if (!immediate) {result = func.apply(context, args);if (!timeout) context = args = null;}}};
新聞熱點(diǎn)
疑難解答
圖片精選