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


Posted in Vue.js onDecember 02, 2020

今天使用节流函数的时候遇见了一个问题,搞了半天才找到原因,所以在这里做个总结。

节流函数

浏览器的一些事件,如:resize,scroll,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用,加重浏览器的负担,导致用户体验非常糟糕。所以先贤们发明了节流函数,简单版本如下:

function throttle (f, wait = 200) {
 let last = 0
 return function (...args) {
 let now = Date.now()
 if (now - last > wait) {
  last = now
  f.apply(this, args)
 }
 }
}

假设有一个 vue 组件 svgMark。这个组件中渲染的元素要在页面窗口大小发生变化时重绘 reDraw ,而重绘时要使用节流函数防止性能损耗。正常情况下代码如下:

<template>
 <div>{{ index }}</div>
</template>

<script>
import { throttle } from 'lodash'
export default {
 name: 'SvgMark',
 data() {
 return {
  index: 0
 }
 },
 mounted() {
 window.addEventListener('resize', this.reDraw)
 },
 beforeDestroy() {
 window.removeEventListener('resize', this.reDraw)
 },
 methods: {
 reDraw: throttle(function() {
  this.index++
 }, 500)
 }
}
</script>
</script>

一般情况下这样用没什么问题。但是有这样一个场景,使用节流函数时却失效了,即当这个组件被 v-for 循环加载了很多次:

<template>
 <div>
 <svgMark v-for="item in 10" :key="item.id" />
 </div>
</template>

这个时候无论渲染了多少个 svgMark 组件,在窗口大小改变的时候却只触发了第一个组件和第 n 割组件的重绘,为什么其他组件没有触发呢?这就要从头说起了。

  • 节流函数

节流函数在初始化的时候产生了一个闭包,闭包内保存了变量 last ,这个 last 记录了上一次执行 f 函数的时间。而当下一次触发节流函数的时候,如果此时时间 now 减去上次时间 last 小于了我们规定的节流时间 wait ,那么函数 f 将不会执行。

很显然,第一个子组件在触发节流函数的时候产生了一个 last,而第二个组件在触发节流函数时候的时产生的 now 并没有满足 now - last > wait 的条件,所以没有执行重绘代码。而到了第 n 个组件触发节流函数的时候,满足了 now - last > wait 的条件所以重绘成功了。

  • vue 组件

vue 组件在代码编译的阶段,组件 svgMark 中的方法 reDraw: throttle(function() { this.index++ }, 500) 就已经被编译成了类似如下函数:

reDraw: ƒ (...args) {
 let now = Date.now()
 if (now - last > wait) {
 last = now
 f.apply(this, args)
 }
}

由于函数是引用类型,所有使用子组件 svgMark 的 methods 中的 reDraw 都指向了同一个内存地址,也就是说所有子组件的 reDraw 方法都是同一个函数。

因为所有组件都公用了同一个节流函数,当然就会产生节流了。那怎么解决问题呢?对症下药就要让每个组件产生自己的节流函数,而不产生共用。代码如下

子组件:

<template>
 <div>{{ index }}</div>
</template>

<script>
import { throttle } from 'lodash'
export default {
 name: 'SvgMark',
 data() {
 return {
  index: 0
 }
 },
 mounted() {
 this.reDraw = throttle(() => {
  this.index++
 }, 500)
 window.addEventListener('resize', this.reDraw)
 },
 beforeDestroy() {
 window.removeEventListener('resize', this.reDraw)
 }
}
</script>

我们在 mounted 声明周期函数中手动声明了 reDraw 函数替代 methods 中的 reDraw ,这样在每个组件初始化的时候都会产生一个自己的节流函数了。需要注意此时节流函数的参数使用了箭头函数,因为这样 this 才会指向组件实例。

以上就是节流函数带给我的坑,现在分享给大家。[下班][鼓掌]

以上就是vue组件中节流函数的失效和解决方法的详细内容,更多关于vue 组件节流函数的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
详解vue 组件注册
Nov 20 Vue.js
vue中利用three.js实现全景图的完整示例
Dec 07 Vue.js
通过vue.extend实现消息提示弹框的方法记录
Jan 07 Vue.js
vue自定义组件实现双向绑定
Jan 13 Vue.js
vue+echarts实现中国地图流动效果(步骤详解)
Jan 27 Vue.js
Vue单页面应用中实现Markdown渲染
Feb 14 Vue.js
vue实现简单数据双向绑定
Apr 28 Vue.js
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
Jun 09 Vue.js
vue3中provide && inject的使用
Jul 01 Vue.js
Vue全局事件总线你了解吗
Feb 24 Vue.js
在vue中import()语法不能传入变量的问题及解决
Apr 01 Vue.js
vue中this.$http.post()跨域和请求参数丢失的解决
Apr 08 Vue.js
Vue3+elementui plus创建项目的方法
Dec 01 #Vue.js
Vue.js桌面端自定义滚动条组件之美化滚动条VScroll
Dec 01 #Vue.js
vue开发chrome插件,实现获取界面数据和保存到数据库功能
Dec 01 #Vue.js
vue实现表格合并功能
Dec 01 #Vue.js
vue element实现表格合并行数据
Nov 30 #Vue.js
Vue Elenent实现表格相同数据列合并
Nov 30 #Vue.js
vue中defineProperty和Proxy的区别详解
Nov 30 #Vue.js
You might like
PHP 字符串 小常识
2009/06/05 PHP
php循环检测目录是否存在并创建(循环创建目录)
2011/01/06 PHP
实现PHP+Mysql无限分类的方法汇总
2015/03/02 PHP
Thinkphp实现自动验证和自动完成
2015/12/19 PHP
解析PHP的Yii框架中cookie和session功能的相关操作
2016/03/17 PHP
php基于openssl的rsa加密解密示例
2016/07/11 PHP
php强制下载文件函数
2016/08/24 PHP
PHP 应用容器化以及部署方法
2018/02/12 PHP
PHP基于session.upload_progress 实现文件上传进度显示功能详解
2019/08/09 PHP
JavaScript 继承的实现
2009/07/09 Javascript
js获取图片长和宽度的代码
2009/11/24 Javascript
jquery判断小数点两位和自动删除小数两位后的数字
2014/03/19 Javascript
jQuery移除元素自动解绑事件实现思路及代码
2014/05/31 Javascript
JavaScript中length属性的使用方法
2015/06/05 Javascript
jquery解析XML及获取XML节点名称的实现代码
2016/05/18 Javascript
详解VUE 对element-ui中的ElTableColumn扩展
2018/03/28 Javascript
Express本地测试HTTPS的示例代码
2018/06/06 Javascript
详解解决使用axios发送json后台接收不到的问题
2018/06/27 Javascript
详解Vue webapp项目通过HBulider打包原生APP(vue+webpack+HBulider)
2019/02/02 Javascript
Vue + Scss 动态切换主题颜色实现换肤的示例代码
2020/04/27 Javascript
vue项目打包后请求地址错误/打包后跨域操作
2020/11/04 Javascript
[01:06:54]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第一场 1月24日
2021/03/11 DOTA
[54:43]DOTA2-DPC中国联赛 正赛 CDEC vs Dynasty BO3 第一场 2月22日
2021/03/11 DOTA
详解在Python程序中使用Cookie的教程
2015/04/30 Python
python实现报表自动化详解
2017/11/16 Python
python实现校园网自动登录的示例讲解
2018/04/22 Python
Python 多维List创建的问题小结
2019/01/18 Python
Python制作动态字符图的实例
2019/01/27 Python
Python django框架开发发布会签到系统(web开发)
2020/02/12 Python
tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this T
2020/06/22 Python
澳大利亚巧克力花束和礼品网站:Tastebuds
2019/03/15 全球购物
大学生求职自荐信
2013/12/12 职场文书
2014大学生全国两会学习心得体会
2014/03/13 职场文书
《雕塑之美》教学反思
2014/04/24 职场文书
法学专业大学生实习自我鉴定
2014/10/05 职场文书
一定要知道的 25 个 Vue 技巧
2021/11/02 Vue.js