vue使用節(jié)流函數(shù)的踩坑實例指南
前言
一個常見的業(yè)務(wù)場景,我們要在input搜索框輸入結(jié)束后,發(fā)送相關(guān)請求,獲取搜索數(shù)據(jù)。頻繁的事件觸發(fā)會導(dǎo)致接口請求過于頻繁。所以需要我們對此加以限制,來禁止不必要的請求,以免資源的浪費~
舉一個🌰 業(yè)務(wù)場景
概念:
使用示例:
function debounce(fn) { let timeout = null; // 創(chuàng)建一個標(biāo)記用來存放定時器的返回值 return function () { clearTimeout(timeout); // 每當(dāng)用戶輸入的時候把前一個 setTimeout clear 掉 timeout = setTimeout(() => { // 然后又創(chuàng)建一個新的 setTimeout, 這樣就能保證輸入字符后的 // interval 間隔內(nèi)如果還有字符輸入的話,就不會執(zhí)行 fn 函數(shù) fn.apply(this, arguments); }, 500); }; } function sayHi() { console.log('防抖成功'); } var inp = document.getElementById('inp'); inp.addEventListener('input', debounce(sayHi)); // 防抖
在vue中使用?
首先說一下之前的踩坑行為
下面的代碼為簡易版的一個場景
function debounce(fn) { let timeout = null; // 創(chuàng)建一個標(biāo)記用來存放定時器的返回值 return function () { clearTimeout(timeout); // 每當(dāng)用戶輸入的時候把前一個 setTimeout clear 掉 timeout = setTimeout(() => { // 然后又創(chuàng)建一個新的 setTimeout, 這樣就能保證輸入字符后的 // interval 間隔內(nèi)如果還有字符輸入的話,就不會執(zhí)行 fn 函數(shù) fn.apply(this, arguments); }, 500); }; }
錯誤的使用方式
<template> <div class="search-view"> <div class="header"> <Search class="search-box" v-model='searchValue' @input='getSearchResult' placeholder='搜索想要的好物' /> <span @click="goBack" class="cancel">取消</span> </div> <div class="serach-view-content" /> </div> </template> <script> import Search from './components/Search'; import debounce from './config'; export default { name: 'SearchView', components: { Search }, data() { return { searchValue: '' }; }, methods: { getSearchResult() { debounce(function() { console.log(this.searchValue); })(); } } }; </script>
為什么錯誤?
源碼層級分析
vue模板編譯 的解析事件
export const onRE = /^@|^v-on:/ export const dirRE = /^v-|^@|^:/ function processAttrs (el) { const list = el.attrsList let i, l, name, value, modifiers for (i = 0, l = list.length; i < l; i++) { name = list[i].name value = list[i].value if (dirRE.test(name)) { // 解析修飾符 modifiers = parseModifiers(name) if (modifiers) { name = name.replace(modifierRE, '') } if (onRE.test(name)) { // v-on name = name.replace(onRE, '') addHandler(el, name, value, modifiers, false, warn) } } } }
總結(jié): 實例初始化階段調(diào)用的初始化事件函數(shù)initEvents實際上初始化的是父組件在模板中使用v-on或@注冊的監(jiān)聽子組件內(nèi)觸發(fā)的事件
vue的事件機(jī)制
Vue.prototype.$on = function(event, fn) { const vm = this; if (Array.isArray(event)) { for (let i = 0; i < event.length; i++) { this.$on(event[i], fn); } } else { //這個_events屬性就是用來作為當(dāng)前實例的事件中心,所有綁定在這個實例上的事件都會存儲在事件中心_events屬性中。 (vm._events[event] || (vm._events[event] = [])).push(fn); } return vm; }; Vue.prototype.$emit = function(event) { const vm = this; let cbs = vm._events[event]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; let args = toArray(arguments, 1); for (let i = 0; i < cbs.length; i++) { try { cbs[i].apply(vm, args); } catch (e) { handleError(e, vm, `event handler for "${event}"`); } } } return vm; };
vue的initState中 調(diào)用了initMethods方法
initMethods中掛在methods方法到this上
for (const key in methods) { if (process.env.NODE_ENV !== 'production') { if (methods[key] == null) { warn( `Method "${key}" has an undefined value in the component definition. ` + `Did you reference the function correctly?`, vm ); } // 如果和props中某個屬性名重名了 拋出異常 if (props && hasOwn(props, key)) { warn(`Method "${key}" has already been defined as a prop.`, vm); } /* 如果methods中某個方法名如果在實例vm中已經(jīng)存在并且方法名是以_或$開頭的,就拋出異常: 提示用戶方法名命名不規(guī)范 */ if (key in vm && isReserved(key)) { warn( `Method "${key}" conflicts with an existing Vue instance method. ` + `Avoid defining component methods that start with _ or $.` ); } // 將method綁定到實例 vm上 這樣我們就可以通過this.xxx 來訪問了 // 同時如果在vue中 let m1 = this.xxx m1() this也指向vue vm[key] = methods[key] == null ? noop : bind(methods[key], vm); }
劃重點:
- 子組件$emit('input事件')
- 父組件接收事件
getSearchResult.apply(this, agrs) <===> apply的調(diào)用可以寫成下面的形式 this.getSearchResult(args) // 進(jìn)而變成這種執(zhí)行 debounce(function() { console.log(this.searchValue); })(); // 這里的debounce 返回了一個函數(shù) 于是變成 (function (fn) { clearTimeout(timeout); // 每當(dāng)用戶輸入的時候把前一個 setTimeout clear 掉 timeout = setTimeout(() => { // 然后又創(chuàng)建一個新的 setTimeout, 這樣就能保證輸入字符后的 // interval 間隔內(nèi)如果還有字符輸入的話,就不會執(zhí)行 fn 函數(shù) fn.apply(this, arguments); }, 500); })() // 到這里 其實就變成了匿名函數(shù)的自執(zhí)行 // 由于每次觸發(fā)input都會返回一個新的匿名函數(shù) 生成一個新的函數(shù)執(zhí)行棧 所以防抖失效~
那么應(yīng)該如何調(diào)用
<template> <div class="search-view"> <div class="header"> <Search class="search-box" v-model='searchValue' @input='getSearchResult()' placeholder='搜索想要的好物' /> <span @click="goBack" class="cancel">取消</span> </div> <div class="serach-view-content"> </div> </div> </template> <script> import debounce from 'lodash.debounce'; export default { name: 'SearchView', components: { Search, }, data() { return { searchValue: '', }; }, methods: { getSearchResult: debounce(function () { console.log(this.searchValue); }, 500), }, }; </script>
分析執(zhí)行過程
getSearchResult().apply(this, args) <===> 忽略參數(shù)行為 只關(guān)注執(zhí)行棧 let func = function () { clearTimeout(timeout); // 每當(dāng)用戶輸入的時候把前一個 setTimeout clear 掉 timeout = setTimeout(() => { // 然后又創(chuàng)建一個新的 setTimeout, 這樣就能保證輸入字符后的 // interval 間隔內(nèi)如果還有字符輸入的話,就不會執(zhí)行 fn 函數(shù) fn.apply(this, arguments); }, 500); }; this.func(args) <===>
子組件觸發(fā)input的行為 返回的始終是一個同一個函數(shù)體 防抖成功
類比于文章開始時介紹的addEventListener
總結(jié)
到此這篇關(guān)于vue使用節(jié)流函數(shù)踩坑的文章就介紹到這了,更多相關(guān)vue節(jié)流函數(shù)踩坑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue項目中input框focus時不調(diào)出鍵盤問題的解決
這篇文章主要介紹了Vue項目中input框focus時不調(diào)出鍵盤問題的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04Vue?Baidu?Map之自定義點圖標(biāo)bm-marker的示例
這篇文章主要介紹了Vue?Baidu?Map之自定義點圖標(biāo)bm-marker,文中給大家介紹了vue-baidu-api地圖標(biāo)記點(自定義標(biāo)記圖標(biāo)),設(shè)置標(biāo)記點的優(yōu)先級問題,結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08Vue中對比scoped css和css module的區(qū)別
這篇文章主要介紹了Vue中scoped css和css module的區(qū)別對比,scoped css可以直接在能跑起來的vue項目中使用而css module需要增加css-loader配置才能生效。具體內(nèi)容詳情大家參考下本文2018-05-05Element el-table的formatter和scope?template不能同時存在問題解決辦法
本文主要介紹了ElementUI?el-table?的?formatter?和?scope?template?不能同時存在問題解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08