vue使用节流函数的踩坑实例指南


Posted in Vue.js onMay 20, 2021

前言

一个常见的业务场景,我们要在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, 这样就能保证输入字符后的
                // interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
                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)
      }
    }
  }
}

总结: 实例初始化阶段调用的初始化事件函数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 !== '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中已经存在并且方法名是以_或$开头的,就抛出异常:
            提示用户方法名命名不规范
            */
            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(function() {
      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 = setTimeout(() => {
        // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的
        // interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
        fn.apply(this, arguments);
    }, 500);
};

this.func(args)

<===>

子组件触发input的行为  返回的始终是一个同一个函数体  防抖成功

类比于文章开始时介绍的addEventListener

总结

到此这篇关于vue使用节流函数踩坑的文章就介绍到这了,更多相关vue节流函数踩坑内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Vue.js 相关文章推荐
vue element-ul实现展开和收起功能的实例代码
Nov 25 Vue.js
vue实现广告栏上下滚动效果
Nov 26 Vue.js
用vue设计一个日历表
Dec 03 Vue.js
浅析vue中的nextTick
Dec 28 Vue.js
vue自定义组件实现双向绑定
Jan 13 Vue.js
使用vue3重构拼图游戏的实现示例
Jan 25 Vue.js
解读Vue组件注册方式
May 15 Vue.js
Vue CLI中模式与环境变量的深入详解
May 30 Vue.js
vue Element-ui表格实现树形结构表格
Jun 07 Vue.js
解决Vue+SpringBoot+Shiro跨域问题
Jun 09 Vue.js
Vue中Object.assign清空数据报错的解决方案
Mar 03 Vue.js
vue 数字翻牌器动态加载数据
Apr 20 Vue.js
vue实现同时设置多个倒计时
May 20 #Vue.js
Vue和Flask通信的实现
Vue Element UI自定义描述列表组件
使用这 6个Vue加载动画库来减少我们网站的跳出率
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
详解vue中v-for的key唯一性
解读Vue组件注册方式
May 15 #Vue.js
You might like
require(),include(),require_once()和include_once()区别
2008/03/27 PHP
swfupload 多文件上传实现代码
2008/08/27 PHP
PHP 开发环境配置(Zend Studio)
2010/04/28 PHP
PHP+SQL 注入攻击的技术实现以及预防办法
2011/01/27 PHP
PHP中strnatcmp()函数“自然排序算法”进行字符串比较用法分析(对比strcmp函数)
2016/01/07 PHP
JavaScript静态的动态
2006/09/18 Javascript
JQuery中$之选择器用法介绍
2011/04/05 Javascript
JQuery入门——移除绑定事件unbind方法概述及应用
2013/02/05 Javascript
node.js中的url.format方法使用说明
2014/12/10 Javascript
javascript实现移动端上的触屏拖拽功能
2016/03/04 Javascript
Linux下为Node.js程序配置MySQL或Oracle数据库的方法
2016/03/19 Javascript
js仿QQ邮箱收件人选择与搜索功能
2017/02/10 Javascript
详解用node-images 打造简易图片服务器
2017/05/08 Javascript
VueJs 将接口用webpack代理到本地的方法
2017/11/27 Javascript
JQuery 又谈ajax局部刷新
2017/11/27 jQuery
vue 实现数字滚动增加效果的实例代码
2018/07/06 Javascript
Vue头像处理方案小结
2018/07/26 Javascript
Vue中用props给data赋初始值遇到的问题解决
2018/11/27 Javascript
实例介绍JavaScript中多种组合继承
2019/01/20 Javascript
微信小程序picker组件关于objectArray数据类型的绑定方法
2019/03/13 Javascript
通过实例学习React中事件节流防抖
2019/06/17 Javascript
[49:56]VG vs Optic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python实现从字典中删除元素的方法
2015/05/04 Python
python编码总结(编码类型、格式、转码)
2016/07/01 Python
Python读取本地文件并解析网页元素的方法
2018/05/21 Python
pycharm重置设置,恢复默认设置的方法
2018/10/22 Python
python selenium firefox使用详解
2019/02/26 Python
浅谈Python3 numpy.ptp()最大值与最小值的差
2019/08/24 Python
工作失误检讨书范文大全
2014/01/13 职场文书
学生会副主席竞聘书
2014/03/31 职场文书
《陈毅探母》教学反思
2014/05/01 职场文书
小学综合实践活动总结
2014/07/07 职场文书
2014年教师节红领巾广播稿
2014/09/10 职场文书
森马旗舰店双十一营销方案
2014/09/29 职场文书
初中生散播谣言检讨书
2014/11/17 职场文书
教你如何让spark sql写mysql的时候支持update操作
2022/02/15 MySQL