1. 程式人生 > 程式設計 >vue使用節流函式的踩坑例項指南

vue使用節流函式的踩坑例項指南

前言

一個常見的業務場景,我們要在input搜尋框輸入結束後,傳送相關請求,獲取搜尋資料。頻繁的事件觸發會導致介面請求過於頻繁。所以需要我們對此加以限制,來禁止不必要的請求,以免資源的浪費~

舉一個🌰 業務場景

vue使用節流函式的踩坑例項指南

概念:

關於防抖函式的介紹

關於addEventListener

使用示例:

    function debounce(fn) {
        let timeout = null; // 建立一個標記用來存放定時器的返回值
        return function () {
            clearTimeout(timeout); // 每當使用者輸入的時候把前一個 setTimeout clear 掉
            timeout = setTimeout(() => {
                // 然後又建立一個新的 setTimeout,這樣就能保證輸入字元後的
                // interval 間隔內如果還有字元輸入的話,就不會執行 fn 函式
                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; // 建立一個標記用來存放定時器的返回值
        return function () {
            clearTimeout(timeout); // 每當使用者輸入的時候把前一個 setTimeout clear 掉
            timeout = setTimeout(() => {
                // 然後又建立一個新的 setTimeout,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,http://www.cppcns.comvalue,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,value,modifiers,false,warn)
      }
    }
  }
}

總結: 例項初始化階段呼叫的初始化事件函式initEvents實際上初始化的是父元件在模板中使用v-on或@註冊的監聽子元件內觸發的事件

vue的事件機制

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屬性就是用來作為當前例項的事件中心,所有繫結在這個例項上的事件都會儲存在事件中心_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中 呼叫了initMethods方法

initMethods中掛在methods方法到this上

for (const key in methods) {
        if (process.env.NODE_ENV !== 'productihttp://www.cppcns.comon') {
            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中已經存在並且方法名是以_或$開頭的,就丟擲異常:
            提示使用者方法名命名不規範
            */
            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的呼叫可以寫成下面的形式
this.getSearchResult(args)

// 進而變成這種執行
debounce(http://www.cppcns.comfunction() {
      console.log(this.searchValue);
})();

// 這裡的debounce 返回了一個函式 於是變成
(function (fn) {
      clearTimeout(timeout); // 每當使用者輸入的時候把前一個 setTimeout clear 掉
      timeout = setTimeout(() => {
          // 然後又建立一個新的 setTimeout,這樣就能保證輸入字元後的
          // interval 間隔內如果還有字元輸入的話,就不會執行 fn 函式
          fn.apply(this,arguments);
      },500);
})()
// 到這裡  其實就變成了匿名函式的自執行
// 由於每次觸發input都會返回一個新的匿名函式  生成一個新的函式執行棧  所以防抖失效~

那麼應該如何呼叫

<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>

分析執行過程

getSearchResult().apply(this,args)
<===> 忽略引數行為 只關注執行棧

let func = function () {
    clearTimeout(timeout); // 每當使用者輸入的時候把前一個 setTimeout clear 掉
    timeout = setT程式設計客棧imeout(() => {
        // 然後又建立一個新的 setTimeout,這樣就能保證輸入字元後的
        // interval 間隔內如果還有字元輸入的話,就不會執行 fnXUInW 函式
        fn.apply(this,arguments);
    },500);
};

this.func(args)

<===>

子元件觸發input的行為 返回的始終是一個同一個函式體 防抖成功

類比於文章開始時介紹的addEventListener

總結

到此這篇關於vue使用節流函式踩坑的文章就介紹到這了,更多相關vue節流函式踩坑內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!