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 的 v-model用法实例
Nov 23 Vue.js
Vue Elenent实现表格相同数据列合并
Nov 30 Vue.js
Vue实现图书管理小案例
Dec 03 Vue.js
在vue项目中封装echarts的步骤
Dec 25 Vue.js
Vue中强制组件重新渲染的正确方法
Jan 03 Vue.js
Vuex实现简单购物车
Jan 10 Vue.js
详解为什么Vue中的v-if和v-for不建议一起用
Jan 13 Vue.js
Vue中引入svg图标的两种方式
Jan 14 Vue.js
Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤
Jan 22 Vue.js
vue集成一个支持图片缩放拖拽的富文本编辑器
Jan 29 Vue.js
vue 实现弹窗关闭后刷新效果
Apr 08 Vue.js
vue实现在data里引入相对路径
Jun 05 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
虫族 Zerg 热键控制
2020/03/14 星际争霸
php压缩多个CSS为一个css的代码并缓存
2011/04/21 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
Zend Framework入门教程之Zend_Session会话操作详解
2016/12/08 PHP
php实现购物车功能(以大苹果购物网为例)
2017/03/09 PHP
Thinkphp结合ajaxFileUpload实现异步图片传输示例
2017/03/13 PHP
PHP封装的PDO数据库操作类实例
2017/06/21 PHP
判断文件是否正在被使用的JS代码
2013/12/21 Javascript
JavaScript中textRange对象使用方法小结
2015/03/24 Javascript
JavaScript获取图片像素颜色并转换为box-shadow显示
2016/03/11 Javascript
Angular 理解module和injector,即依赖注入
2016/09/07 Javascript
bootstrap jquery dataTable 异步ajax刷新表格数据的实现方法
2017/02/10 Javascript
vue 动态改变静态图片以及请求网络图片的实现方法
2018/02/07 Javascript
vue.js或js实现中文A-Z排序的方法
2018/03/08 Javascript
解决layui数据表格table的横向滚动条显示问题
2019/09/04 Javascript
解决vue2中使用elementUi打包报错的问题
2020/09/22 Javascript
调试Python程序代码的几种方法总结
2015/04/28 Python
简单了解python PEP的一些知识
2019/07/13 Python
python实现知乎高颜值图片爬取
2019/08/12 Python
Python实现网页截图(PyQT5)过程解析
2019/08/12 Python
Pandas将列表(List)转换为数据框(Dataframe)
2020/04/24 Python
python 绘制场景热力图的示例
2020/09/23 Python
python利用opencv实现颜色检测
2021/02/23 Python
浅析移动设备HTML5页面布局
2015/12/01 HTML / CSS
美国婚礼和派对礼品网站:Kate Aspen(新娘送礼会、迎婴派对)
2018/03/28 全球购物
乌克兰品牌化妆品和香水在线商店:Bomond
2020/01/14 全球购物
如何开启linux的ssh服务
2013/06/03 面试题
写演讲稿所需要注意的4个条件
2014/01/09 职场文书
《吃水不忘挖井人》教学反思
2014/04/15 职场文书
会计系毕业求职信
2014/08/07 职场文书
年终晚会活动方案
2014/08/21 职场文书
布达拉宫的导游词
2015/02/02 职场文书
2015年医院科室工作总结范文
2015/05/26 职场文书
在校学生证明格式
2015/06/24 职场文书
「回转企鹅罐」10周年纪念展「輪るピングドラム展」海报公开
2022/03/22 日漫
python神经网络ResNet50模型
2022/05/06 Python