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

前言

一个常见的业务场景,我们要在input搜索框输入结束后,发送相关请求,获取搜索数据。频繁的事件触发会导致接口请求过于频繁。所以需要我们对此加以限制,来禁止不必要的请求,以免资源的浪费~

举一个🌰 业务场景

概念:

关于防抖函数的介绍

关于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节流函数踩坑内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈VUE防抖与节流的最佳解决方案(函数式组件)

    前言 有echarts使用经验的同学可能遇到过这样的场景,在window.onresize事件回调里触发echartsBox.resize()方法来达到重绘的目的,resize事件是连续触发的这意味着echarts实例会连续的重绘这是非常耗性能的.还有一个常见的场景在input标签的input事件里请求后端接口,input事件也是连续触发的,假设我输入了"12"就会请求两次接口参数分别是"1"和"12",比浪费网络资源更要命的是如果参数为&quo

  • vue实现输入框的模糊查询的示例代码(节流函数的应用场景)

    上一篇讲到了javascript的节流函数和防抖函数,那么我们在实际场合中该如何运用呢? 首先,我们来理解一下:节流函数首先是节流,就是节约流量.内存的损耗,旨在提升性能,在高频率频发的事件中才会用到,比如:onresize,onmousemove,onscroll,oninput等事件中会用到节流函数: 输入框的模糊查询功能原理分析 所谓模糊查询就是不需要用户完整的输入或者说全部输入信息即可提供查询服务,也就是用户可以在边输入的同时边看到提示的信息(其实是查询出来匹配到的信息),百度的搜索功能

  • Vue中函数防抖节流的理解及应用实现

    防抖和节流的目的都是为了减少不必要的计算,不浪费资源,只在适合的时候再进行触发计算. 一.函数防抖 定义 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时:典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时. 实现原理 函数防抖的基本思想是设置一个定时器,在指定时间间隔内运行代码时清楚上一次的定时器,并设置另一个定时器,知道函数请求停止并超过时间间隔才会执行. 使用场景 文本框输入搜索(连续输入时避免多次请求接口) 代码实现 /** * 函数防抖

  • vue组件中节流函数的失效的原因和解决方法

    今天使用节流函数的时候遇见了一个问题,搞了半天才找到原因,所以在这里做个总结. 节流函数 浏览器的一些事件,如:resize,scroll,mousemove等.这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用,加重浏览器的负担,导致用户体验非常糟糕.所以先贤们发明了节流函数,简单版本如下: function throttle (f, wait = 200) { let last = 0 return function (...args) { let now = Date.no

  • Vue自定义指令封装节流函数的方法示例

    节流函数是web前端开发中经常用到的一个开发技巧,在input实时搜索,滚动事件等,为了避免过多消耗性能,我们都会使用节流函数.在<JavaScript高级程序设计>一书中有这样的一个例子: function throttle (method, context) { clearTimeout((method.tId)) method.tId = setTimeout(function () { method.call(context) }, 100) } function resizeDiv

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

    前言 一个常见的业务场景,我们要在input搜索框输入结束后,发送相关请求,获取搜索数据.频繁的事件触发会导致接口请求过于频繁.所以需要我们对此加以限制,来禁止不必要的请求,以免资源的浪费~ 举一个

  • 详解Vue微信公众号开发踩坑全记录

    本文介绍了Vue微信公众号开发踩坑全记录,分享给大家,也给自己留个笔记. 需求 微信授权登录(基于公众号的登录方案) 接入JS-SDK实现图片上传,分享等功能 现状及难点 采用的Vue框架,前后端分离模式(vue工程仅作为客户端),用户通过域名访问的是客户端,但是微信授权中涉及签名和token校验依赖服务端 JS-SDK需要向服务端获取签名,且获取签名中需要的参数包括所在页面的url,但由于单页应用的路由特殊,其中涉及到IOS和android微信客户端浏览器内核的差异性导致的兼容问题 解决方案

  • vue 系列——vue2-webpack2框架搭建踩坑之路

    react.vue.angular代表了3种前端工程化的思想,学习三大框架主要是理解它们的核心概念,比如组件.生命周期.单向数据流.双向绑定等.这些概念在非框架开发中,很少人会去这样系统化的思考,对于新手来说,很多概念都没有接触过,不知道从何入手一个react.vue或者是angular项目,下面我将会从零搭建vue项目,边做项目边学习vue的思想. 1.想要使用vue,我首先该怎么做? 想要学习vue,我第一件事是去vue官网看简介:https://cn.vuejs.org/v2/guide.

  • python中remove函数的踩坑记录

    摘要: 在python的使用过程中,难免会遇到要移除列表中对象的要求.这时可以使用remove函数. 对于python中的remove()函数,官方文档的解释是:Remove first occurrence of value.大意也就是移除列表中等于指定值的第一个匹配的元素. 语法 list.remove() 参数 obj 参数:从列表中删除的对象的索引 返回值 删除后不会返回值 常见用法: a = [1,2,3,4],a.remove(1),然后a就是[2,3,4]:对于a = [1,1,1

  • vue使用 vue-socket.io三种方式及踩坑实例解析

    目录 前言 安装 使用方式一 (官方用法)[全局挂载,不验证] 使用方式二 (组件挂载使用)[可验证] 使用方式三 (全局挂载使用)[可验证] 使用方式推荐 nodejs服务端本地demo代码 总结 前言 vue项目实时通信实现常用方式: 一.原生HTML5 WebSocket实现,vue中使用websocket 二.插件socket.io官网 ,Socket.io是一个WebSocket库,包括了客户端js和服务器端的nodejs,会自动根据浏览器从WebSocket.AJAX长轮询.Ifra

  • vue使用Element el-upload 组件踩坑记

    目录 一.基本使用 二.图片数量控制 三.图片格式限制/可以选中多张图片 补充:在vue项目中使用element-ui的Upload上传组件 一.基本使用 最近研究了一下 el-upload组件 踩了一些小坑  写起来大家学习一下 很经常的一件事情 经常会去直接拷贝 element的的组件去使用 但是用到上传组件时 就会遇到坑了 如果你直接去使用upload 你肯定会遇见这个错误 而且 上传的图片 可能会突然消失  这时候如果你没有接口  你是完全不知道为什么移除的  所以 无接口时 只能去猜测

  • 详解vantUI框架在vue项目中的应用踩坑

    1.订单提交地址等组件的应用. 使用的组件有如下: import {Card,button,Toast,AddressList,Popup,AddressEdit,Area} from 'vant' 主要是配货地址编辑这块: <van-address-edit :area-list="areaList" :address-info="addressInfo" show-postal show-delete show-set-default show-searc

  • vue结合AntV G2的使用踩坑记录

    目录 vue结合AntV G2使用踩坑 AntV-G2语法总结 目的 第一步语法基础 第二步设置坐标轴的外观与度量 第二步创建view vue结合AntV G2使用踩坑 官网使用import G2 from '@antv/g2';引入但是会报一个 "export 'default' (imported as 'G2') was not found in '@antv/g2'  得错误 找了半天原因,最终解决办法 import * as G2 from '@antv/g2' AntV-G2语法总结

  • Docker 安装Jenkins全过程及踩坑指南

    目录 Docker安装Jenkins 1.安装过程 2.Jenkins安装npm 2.1.替换容器的源 2.2.安装nodejs和npm 3.Jenkins流水线+Docker+Maven+Githubwebhooks+Springboot 3.1.需要的插件和配置 3.1.1.插件 BlueOcean MavenIntegration 3.1.2.配置 3.1.3.安装maven 3.1.4.配置Jenkins使用Docker 3.1.5.配置Jenkins中的GitSSH 3.1.6.Git

随机推荐