当前位置:首页 > 技术分析 > 正文内容

vue3 的 watch,性能真的很差!你们知道怎么优化吗?

最近发现公司某个 Vue 页面非常卡,一看原来是因为页面中使用了大量的 watch ,但是又没有进行防抖或者节流的限制,导致了触发非常频繁,所以页面非常卡顿。

所以想着,想分享一下如何给 watch 加上 防抖、节流。

一、前置知识:防抖与节流

在实现监听器之前,我们需要理解两个核心概念:

  • 防抖(Debounce): 在连续触发时,只在最后一次操作后等待指定时间执行
  • 节流(Throttle): 在连续触发时,保证固定时间间隔内只执行一次

二、基础监听器实现

我们先实现一个简单的响应式监听器,基于Vue的watch函数:

/**
 * 基础监听器
 * @param {Ref} source 要监听的响应式数据
 * @param {Function} callback 变化回调
 * @param {Object} options 监听选项
 */
function basicWatcher(source, callback, options = {}) {
let cleanup = () => {}

const stop = watch(source, (value, oldValue, onCleanup) => {
    // 清除之前的副作用
    cleanup()
    
    // 注册新的清理函数
    onCleanup(() => {
      cleanup = () => {}
    })
    
    // 执行回调
    callback(value, oldValue)
  }, options)

return stop
}

三、实现防抖监听器(watchDebounced)

实现要点:

  • 使用 setTimeout 延迟回调执行
  • 每次新变化时重置定时器
  • 清理函数确保组件卸载时终止等待中的回调
/**
 * 防抖监听器
 * @param {Ref} source 要监听的响应式数据
 * @param {Function} callback 回调函数
 * @param {number} delay 防抖延迟时间(毫秒)
 * @param {Object} options 监听选项
 */
function watchDebounced(source, callback, delay = 300, options = {}) {
let timeoutId = null
let cleanup = () => {}

const stop = watch(source, (value, oldValue, onCleanup) => {
    // 清除之前的定时器和副作用
    clearTimeout(timeoutId)
    cleanup()

    // 设置新的定时器
    timeoutId = setTimeout(() => {
      // 执行回调时绑定正确的this上下文
      callback.call(this, value, oldValue)
    }, delay)

    // 注册清理函数
    onCleanup(() => {
      clearTimeout(timeoutId)
      cleanup = () => {}
    })
  }, options)

return stop
}

四、实现节流监听器(watchThrottled)

实现要点:

  • 使用时间戳计算剩余可执行时间
  • 未到间隔时间时,设置剩余时间的定时器
  • 保证间隔时间内至少执行一次
/**
 * 节流监听器
 * @param {Ref} source 要监听的响应式数据
 * @param {Function} callback 回调函数
 * @param {number} interval 节流间隔(毫秒)
 * @param {Object} options 监听选项
 */
function watchThrottled(source, callback, interval = 300, options = {}) {
let lastExecTime = 0
let cleanup = () => {}
let timeoutId = null

const stop = watch(source, (value, oldValue, onCleanup) => {
    const now = Date.now()
    const elapsed = now - lastExecTime

    // 清除等待中的定时器
    clearTimeout(timeoutId)
    cleanup()

    if (elapsed >= interval) {
      // 立即执行
      callback(value, oldValue)
      lastExecTime = now
    } else {
      // 设置剩余时间的定时器
      timeoutId = setTimeout(() => {
        callback(value, oldValue)
        lastExecTime = Date.now()
      }, interval - elapsed)
    }

    onCleanup(() => {
      clearTimeout(timeoutId)
      cleanup = () => {}
    })
  }, options)

return stop
}

五、使用示例

<script setup>
import { ref } from 'vue'

const searchKeyword = ref('')

// 防抖监听示例
watchDebounced(
  searchKeyword,
  (newVal) => {
    console.log('防抖搜索:', newVal)
    // 这里可以执行API请求
  },
  500
)

// 节流监听示例
watchThrottled(
  searchKeyword,
  (newVal) => {
    console.log('节流记录:', newVal)
    // 这里可以执行高频状态记录
  },
  1000
)
</script>

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/3896.html

标签: js 防抖函数
分享给朋友:

“vue3 的 watch,性能真的很差!你们知道怎么优化吗?” 的相关文章

vue中组件之间的通信方式

** 1.1 父子组件**a. 父向子传数据: 第1种: 父通过属性传值,子组件通过props接收数据(注:props传过来的数据是单向的,不可以进行修改)第2种:子组件可以通过$parent来获取父组件里的数据和调用父组件的方法(注:数据是双向的,还要注意如用了UI组件并且在该UI组件里重新定义一...

GitLab-创建分支

描述分支是独立的生产线,是开发过程的一部分。分支的创建涉及以下步骤。创建一个分支步骤1-登录您的GitLab帐户,然后转到“ 项目”部分下的项目。步骤2-要创建分支,请单击“ 存储库”部分下的“ 分支”选项,然后单击“ 新建分支”按钮。步骤3-在“ 新建分支”屏幕中,输入分支的名称,然后单击“ 创建...

学无止境:Git 如何优雅地回退代码

来源:https://zhenbianshu.github.io前言从接触编程就开始使用 Git 进行代码管理,先是自己玩 Github,又在工作中使用 Gitlab,虽然使用时间挺长,可是也只进行一些常用操作,如推拉代码、提交、合并等,更复杂的操作没有使用过,看过的教程也逐渐淡忘了,有些对不起 L...

GitLab 14.6发布,优化Geo高可用,安全更新等

昨天,GitLab官方按照管理发布了有一个月度版本GitLab 14.6的发布,这也是本年度收官版本。14.6中在安全合规性方面,在Geo方面以及MD代码块一键复制等方便做了优化,另外还在UI图标方面发布了一套全新的图标。详细情况请和虫虫一起学习。GitLab 14.6主要改进使用 Geo 实现无缝...

编码 10000 个小时后,开发者悟了:“不要急于发布!”

【CSDN 编者按】在软件开发的道路上,时间是最好的老师。根据“一万小时定律”,要成为某个领域的专家,通常需要大约一万小时的刻意练习。本文作者身为一名程序员,也经历了一万小时的编程,最终悟出了一个道理:慢即是快,重视架构设计和代码质量,确保每一行代码都经得起时间的考验。作者 | Sotiris Ko...

7 招教你轻松搭建以图搜图系统

作者 | 小龙责编 | 胡巍巍当您听到“以图搜图”时,是否首先想到了百度、Google 等搜索引擎的以图搜图功能呢?事实上,您完全可以搭建一个属于自己的以图搜图系统:自己建立图片库;自己选择一张图片到库中进行搜索,并得到与其相似的若干图片。Milvus 作为一款针对海量特征向量的相似性检索引擎,旨在...