最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue组件中节流函数的失效和解决

    正文概述 掘金(幻灵尔依)   2020-12-02   456

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

    节流函数

    浏览器的一些事件,如: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组件中节流函数的失效和解决

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元