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
相关产品推荐
相关产品推荐

