• 在Vue中如何正确使用防抖和节流技术?
  • 发布于 2个月前
  • 100 热度
    0 评论
  • 望北海
  • 0 粉丝 23 篇博客
  •   
前言
今天讲解的主题是在vue中正确使用防抖和节流,先和大家再解释下什么是防抖什么是节流。防抖好比生活中乘坐升降电梯时,你进去了,门需要等两秒才会关门,过去了一秒多的时候,又有人进来了,此时又重新计时两秒,等两秒内没人进来时,才会关门。节流好比玩《王者荣耀》游戏时,攻击速度并不会随着你平A的速度变快而更快,而是按正常该英雄的攻击速度来攻击。这就是即使你手速很快,但是他也只是按正常的攻速攻击,这就是节流。

更通俗的讲就是:
1.防抖犹如 英雄回城,被打断了就得重新花时间回城。
2.节流犹如 技能冷却,得冷却时间到了,才能重新释放技能。

代码
防抖及节流相关代码可查阅 js中防抖与节流的封装
主测试代码如下:
<template>
  <div ref="container" class="app-container">
    <van-sticky class="search-wrap">
      <van-search
        :round="false"
        input-align="left"
        v-model="params.unitName"
        placeholder="请输入搜索关键词"
        @input="handleSearch"
      >
      </van-search>
    </van-sticky>
    <van-pull-refresh
      :class="['content-wrap', refreshing ? 'refreshing' : '']"
      v-model="refreshing"
      @refresh="onRefresh"
    >
      <van-list
        v-model="loading"
        :finished="finished"
        :finished-text="list.length === 0 ? '' : '没有更多了'"
        @load="onLoad"
      >
        <div
          class="news-item"
          @click="handleSelectItem(item)"
          v-for="(item, index) in list"
          :key="index"
        >
          <p class="news-title xz-ellipsis">{{ item.unitName }}</p>
          <p class="time-box flex-c-b">
            <span class="danwei">{{ item.index }}</span>
            <span class="time">{{ item.time }}</span>
          </p>
        </div>

        <van-empty
          class="no-result"
          description="未查询到相关信息,下拉可重新查询"
          v-show="list.length === 0 && !refreshing && !loading"
        />
      </van-list>
    </van-pull-refresh>
  </div>
</template>
<script>
import {debounce} from "@/utils/common"
export default {
  data() {
    return {
      handleSearch: debounce(this.onRefresh, 1000),
      showSearch: false,
      params: {
        unitName: "", // 搜索关键字
        page: 0, //页数
        size: 20 //每行页数
      },
      list: [],
      loading: false,
      finished: false,
      refreshing: false,
      baseForm: {
        unitName: "" // 微信公众号
      }
    }
  },
  methods: {
    handleSelectItem(item) {
      this.baseForm.unitName = item.unitName
      this.showSearch = false
    },
    onLoad() {
      this.params.page++
      this.getListApi(this.params).then((data) => {
        if (this.refreshing || this.isSearch) {
          this.list = []
          this.refreshing = false
          this.isSearch = false
        }
        this.list = this.list.concat(data.list)
        this.loading = false
        if (this.list.length >= data.total) {
          this.finished = true
        }
      })
    },
    onRefresh() {
      this.$refs.container.scrollTop = 0
      this.isSearch = true
      // 清空列表数据
      this.finished = false
      // 重新加载数据
      // 将 loading 设置为 true,表示处于加载状态
      this.loading = true
      this.params.page = 0
      this.onLoad()
    },
    //模拟接口
    getListApi(params) {
      console.log("调用了查询接口,查询页码为:")
      console.log(params.page)
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          const data = []
          const total = 50
          for (let i = 0; i < params.size; i++) {
            if ((params.page - 1) * params.size + i < total) {
              data.push({
                index: i + (params.page - 1) * params.size,
                url: "/consultation-detail",
                unitName: "关注微信公众号【爆米花小布】,提升前端开发效率",
                time: "2022-09-16 20:00:00"
              })
            } else {
              continue
            }
          }
          resolve({
            list: data,
            total: total
          })
        }, 500)
      })
    }
  }
}
</script>
<style lang="less" scoped>
.app-container {
  position: relative;
  height: 100vh;  // 关键
  overflow: auto; // 关键
  padding: 0;
  // 搜索容器
  .search-wrap {
    width: 100%;
    background-color: #fff;
    z-index: 9999;
    position: relative;
    border-bottom: 1px solid #eee;
     // 堆代码 duidaima.com
    // 优化条件搜索样式
    /deep/ .van-dropdown-menu__bar {
      height: 40px;
      box-shadow: none;

      .van-dropdown-menu__title {
        color: #212121;
        font-size: 14px;
      }
    }

    .main-text {
      color: #4077f4;
    }
  }
  // 内容列表容器
  .content-wrap {
    padding: 16px;
    min-height: calc(100vh - 45px);
    &.refreshing {
      // 解决下拉刷新时双加载动画问题
      /deep/ .van-list__loading {
        display: none;
      }
    }

    /deep/ .van-empty__description {
      padding: 0;
    }
    .news-item {
      margin-top: 0 16px;
      border-bottom: 1px solid #f9faf9;
      &:last-child {
        border: none;
      }
      .news-title {
        font-size: 16px;
        color: #333333;
        line-height: 22px;
      }
      .time-box {
        font-size: 14px;
        color: #999999;
        padding: 8px 0;
      }
    }
  }
}
</style>
vue项目中使用防抖和节流
第一种:
模板中添加方法
      <van-search
        :round="false"
        input-align="left"
        v-model="params.unitName"
        placeholder="请输入搜索关键词"
        @input="handleSearch"
      >
      </van-search>
在data中定义该方法
  data() {
    return {
      handleSearch: debounce(this.onRefresh, 1000)
    }
  },
onRefresh为防抖的逻辑方法
第二种:
模板中添加方法
      <van-search
        :round="false"
        input-align="left"
        v-model="params.unitName"
        placeholder="请输入搜索关键词"
        @input="handleSearch"
      >
      </van-search>
在methods中添加如下代码
  methods: {
    handleSearch: debounce(function() {
      this.onRefresh()
    }, 1000),
  }
一定要使用function() 不可使用箭头函数,onRefresh得加() 否则没调用到,不触发。

第三种:
在mixin中添加全局防抖方法
  created() {
    this.$debounce = debounce
  },
模板中添加方法
      <van-search
        :round="false"
        input-align="left"
        v-model="params.unitName"
        placeholder="请输入搜索关键词"
        @input="handleSearch">
      </van-search>
 在页面中添加如下代码
  created() {
    this.handleSearch = this.$debounce(this.onRefresh, 1000)
  },
节流同理。
防抖效果如下:


/**
 * 
 * @param {*} func 防抖函数
 * @param {*} wait 等待时长(毫秒)
 * @param {*} immediate 是否先立即触发一次
 * @returns 
 */
function debounce(func, wait = 300, immediate = false) {
  let timer = null
  return function(...arg) {
    if (timer !== null) {
      clearTimeout(timer)
    } else if (immediate) {
      func.apply(this, arg)
    }
    timer = setTimeout(() => {
      func.apply(this, arg)
      timer = null
    }, wait)
  }
}
节流效果如下


/**
 * 
 * @param {*} func 节流函数
 * @param {*} wait 等待时长(毫秒)
 * @param {*} immediate 是否先立即触发一次
 * @returns 
 */
function throttle(func, wait = 300, immediate = false){
  let last, timer
  return function(...arg) {
    const now = +new Date()
     // 堆代码 duidaima.com
    if (last && now < last + wait) {
      clearTimeout(timer)
      timer = setTimeout(function() {
        func.apply(this, arg)
      }, wait) //停止触发 后wait毫秒立即执行
    } else {
      if (!immediate) {
        if (last) {
          func.apply(this, arg)
        }
      } else {
        func.apply(this, arg)
      }
      last = now
    }
  }
}
此节流方法超过了传统定义的节流,因为在wait秒后没触发事项时也会最后再执行一下回调函数。

总结
这就是防抖和节流在vue项目中的使用方式,并附上了防抖和节流封装好的方法,有兴趣的自取。
用户评论