You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue 3如何获取响应式更新的Delta对象以优化性能?

实现Vue响应式对象的更新条目监听(避免遍历全列表)

现有一个全局响应式对象entries,通过useEntries组合式函数提供数据操作方法。当在模板A中更新条目后,希望模板B能仅处理被更新的条目,而非遍历整个列表,同时要求避免额外维护一份更新列表。

注:原代码存在遍历bug——Object.entries(docs)遍历数组会得到[索引, 文档对象]的数组,导致entries[doc.id]赋值错误,需修正为Object.values(docs)。


方案1:通过深度监听自动捕获更新条目

适合条目可能被外部直接修改的场景,利用Vue的watchEffect和onTrigger钩子自动跟踪变化:

import { reactive, computed, ref, watchEffect } from 'vue'

const entries = reactive({}) 
const docs = [
  { id: 1, name: 'foo', value: 'bar' },
  { id: 2, name: 'bar', value: 'moo' }
]

// 修正原代码的遍历问题
Object.values(docs).forEach(doc => {
  entries[doc.id] = doc
})

// 临时存储更新条目
const updatedEntries = ref([])

// 深度监听entries的value属性变化
watchEffect(() => {
  // 访问所有条目的value,让effect跟踪这些属性
  Object.values(entries).forEach(entry => entry.value)
}, {
  deep: true,
  onTrigger: (event) => {
    // 仅处理value属性的更新操作
    if (event.type === 'set' && event.key === 'value') {
      const updatedEntry = event.target
      // 避免重复添加同一条目
      if (!updatedEntries.value.includes(updatedEntry)) {
        updatedEntries.value.push(updatedEntry)
      }
    }
  }
})

export const useEntries = () => {
  const getAll = computed(() => Object.values(entries))
  
  const update = (id, newValue) => {
    entries[id].value = newValue
  }

  // 暴露changes:获取更新条目后自动清空,避免重复处理
  const changes = computed(() => {
    const result = [...updatedEntries.value]
    updatedEntries.value = []
    return result
  })

  return { getAll, update, changes } 
}

模板B中监听changes即可拿到更新的条目:

<script setup>
import { useEntries } from '/path/to/useEntries'
import { watch } from 'vue'

const { changes } = useEntries()

// 同步触发监听,确保拿到最新的更新数据
watch(changes, (updatedItems) => {
  if (updatedItems.length === 0) return
  // 仅处理更新的条目
  updatedItems.forEach(item => {
    console.log(`条目 ${item.id} 的value更新为:${item.value}`)
  })
}, { flush: 'sync' })
</script>

方案2:在更新方法中主动记录条目

适合所有更新都通过useEntries提供的方法进行的场景,性能更优,实现更简单:

import { reactive, computed, ref } from 'vue'

const entries = reactive({}) 
const docs = [
  { id: 1, name: 'foo', value: 'bar' },
  { id: 2, name: 'bar', value: 'moo' }
]

Object.values(docs).forEach(doc => {
  entries[doc.id] = doc
})

const updatedEntries = ref([])

export const useEntries = () => {
  const getAll = computed(() => Object.values(entries))
  
  const update = (id, newValue) => {
    const entry = entries[id]
    entry.value = newValue
    // 记录更新条目,避免重复添加
    if (!updatedEntries.value.includes(entry)) {
      updatedEntries.value.push(entry)
    }
  }

  // 支持批量更新场景
  const batchUpdate = (updates) => {
    updates.forEach(({ id, newValue }) => {
      const entry = entries[id]
      entry.value = newValue
      if (!updatedEntries.value.includes(entry)) {
        updatedEntries.value.push(entry)
      }
    })
  }

  const changes = computed(() => {
    const result = [...updatedEntries.value]
    updatedEntries.value = []
    return result
  })

  return { getAll, update, batchUpdate, changes } 
}

模板B的使用方式与方案1完全一致。


核心思路

两种方案都通过临时的updatedEntries存储更新条目,再通过changes这个computed对外暴露:每次访问changes时返回当前的更新集合并清空数组,确保模板B只会处理一次更新条目,同时避免维护额外的持久化列表。

内容的提问来源于stack exchange,提问作者Jankapunkt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 18:06:05