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实现创建试卷相关功能(实例代码)
Dec 07 Vue.js
Vue 实现一个简单的鼠标拖拽滚动效果插件
Dec 10 Vue.js
如何在vue中使用kindeditor富文本编辑器
Dec 19 Vue.js
Vue 修改网站图标的方法
Dec 31 Vue.js
Vue ​v-model相关知识总结
Jan 28 Vue.js
如何封装Vue Element的table表格组件
Feb 06 Vue.js
vue如何使用rem适配
Feb 06 Vue.js
vue组件的路由高亮问题解决方法
May 11 Vue.js
Vue3.0写自定义指令的简单步骤记录
Jun 27 Vue.js
vue项目中的支付功能实现(微信支付和支付宝支付)
Feb 18 Vue.js
Vue3如何理解ref toRef和toRefs的区别
Feb 18 Vue.js
vue 自定义组件添加原生事件
Apr 21 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
Excel数据导入Mysql数据库的实现代码
2008/06/05 PHP
php 移除数组重复元素的一点说明
2008/11/27 PHP
PHP 日常开发小技巧
2009/09/23 PHP
ThinkPHP3.2.1图片验证码实现方法
2016/08/19 PHP
一个对于Array的简单扩展
2006/10/03 Javascript
js trim函数 去空格函数与正则集锦
2009/11/20 Javascript
验证手机号码的JS方法分享
2013/09/10 Javascript
bootstrap table 服务器端分页例子分享
2015/02/10 Javascript
快速学习jQuery插件 Cookie插件使用方法
2015/12/01 Javascript
jQuery中Ajax全局事件引用方式及各个事件(全局/局部)执行顺序
2016/06/02 Javascript
深入浅析JavaScript的API设计原则
2016/06/14 Javascript
第一次接触神奇的Bootstrap导航条
2016/08/09 Javascript
浅谈JSON.stringify()和JOSN.parse()方法的不同
2016/08/29 Javascript
浅谈js键盘事件全面控制
2016/12/01 Javascript
什么是Vue.js框架 为什么选择它?
2017/10/17 Javascript
基于vue.js快速搭建图书管理平台
2017/10/29 Javascript
swiper插件自定义切换箭头按钮
2017/12/28 Javascript
Vue的状态管理vuex使用方法详解
2020/02/05 Javascript
javascript实现图片轮换动作方法
2020/08/07 Javascript
Element-ui 自带的两种远程搜索(模糊查询)用法讲解
2021/01/29 Javascript
[03:30]DOTA2完美“圣”典精彩集锦
2016/12/27 DOTA
python正则表达式判断字符串是否是全部小写示例
2013/12/25 Python
Python中设置变量作为默认值时容易遇到的错误
2015/04/03 Python
Python中django学习心得
2017/12/06 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
2018/07/27 Python
局域网内python socket实现windows与linux间的消息传送
2019/04/19 Python
Python中栈、队列与优先级队列的实现方法
2019/06/30 Python
keras 如何保存最佳的训练模型
2020/05/25 Python
解决Tensorflow2.0 tf.keras.Model.load_weights() 报错处理问题
2020/06/12 Python
乌克兰排名第一的在线旅游超市:Farvater.Travel
2020/01/02 全球购物
SQL Server面试题
2016/10/17 面试题
勤俭节约倡议书
2014/04/14 职场文书
经营目标管理责任书
2014/07/25 职场文书
2014国庆节餐厅促销活动策划方案
2014/09/16 职场文书
出纳岗位职责范本
2015/03/31 职场文书
Pygame Draw绘图函数的具体使用
2021/11/17 Python