限制 vue/vuex 反应性

Posted

技术标签:

【中文标题】限制 vue/vuex 反应性【英文标题】:Restrict vue/vuex reactivity 【发布时间】:2017-11-02 13:48:21 【问题描述】:

假设我们有一些对象数组,这些对象永远不会改变。例如,这可能是从 google maps places api 接收的搜索结果 - 每个结果都是相当复杂的对象,具有 id、title、address、coordinate、photos 和一堆其他属性和方法。

我们想使用 vue/vuex 在地图上显示搜索结果。如果将一些新结果推送到商店,我们希望在地图上绘制它们的标记。如果某些结果被删除,我们要删除它的标记。但在内部,每个结果都不会改变。

有没有办法告诉 vue 跟踪数组(push、splice 等),但不要更深入,也不跟踪它的任何元素的属性?

现在我只能想象一些丑陋的数据拆分 - 将 id 数组保留在 vue 中,并在存储之外有单独的逐个缓存。我正在寻找更优雅的解决方案(如 knockout.js observableArray)。

【问题讨论】:

【参考方案1】:

您可以在这些对象上使用Object.freeze()。这会带来(非常小的!)性能损失,但如果您不一次添加数百或数千个对象,它应该可以忽略不计。

编辑:或者,您可以冻结数组(性能更好),这将使 Vue 跳过“反应”其内容。

当您需要将对象添加到该数组时,请构建一个新的以替换旧的:

state.searchResults = Object.freeze(state.searchResults.concat([item]))

即使对于更大的阵列,这也相当便宜。

【讨论】:

我有一个疑问,如果searchResults[ ] 处于存储状态,并且我们使用组件中的计算属性从存储中获取searchResults[ ],vue 将在发生更改时更新该数组长度甚至项目属性的任何更改 使用这两种方法,计算属性将在数组内容发生变化时更新。 如果内部每个结果都没有改变,那么计算属性只会在数组中的任何项目被添加或删除时更新。那么,如果您知道数组中的项目没有改变,为什么还要采取额外的步骤并冻结对象 我用另一种解决方案调整了我的答案,即在添加东西时冻结数组并用新的替换它(因为你不能 push() 到冻结的数组)。 一个就够了。 Vue 在遇到冻结对象时会跳过子对象。【参考方案2】:

乍一看,数据拆分似乎不是这个任务的丑陋解决方案。我们所需要的只是使用 getter 而不是原始的 vuex 状态。我们假设传入的结果是一个数组,其中包含具有唯一 id 字段的任何对象。那么解决方案可能如下所示:

const state = 
    ids: []


let resultsCache = ;

const getters = 
    results: function(state) 
        return _.map(state.ids,id => resultsCache[id]);
    


const mutations = 
    replaceResults: function(state,results) 
        const ids = [];
        const cache = ;
        (results||[]).forEach((r) => 
            if (!cache[r.id]) 
                cache[r.id] = r;
                ids.push(r.id);
            
        );
        state.ids = ids;
        resultsCache = cache;
    ,
    appendResults: function(state,results) 
        (results||[]).forEach((r) => 
            if (!resultsCache[r.id]) 
                resultsCache[r.id] = r;
                state.results.push(r.id);
            
        );
    


export default 
    getters,
    mutations,
    namespaced: true

【讨论】:

【参考方案3】:

我从 vue 中创建了一个名为 vue-for-babylonians 的分支来限制反应性,甚至允许某些对象属性是反应性的。看看here。

有了它,你可以告诉 Vue 不要让任何存储在 vue 或 vuex 中的对象成为响应式的。您还可以告诉 Vue 使对象属性的某些子集具有响应性。您会发现性能得到了显着提升,并且您可以像在 vue/vuex 中一样享受存储和传递大型对象的便利。

【讨论】:

【参考方案4】:

您可以使用shallowRef 来实现此目的。

首先导入它:

import shallowRef from 'vue';

在你的突变中,你可以有这样的突变:

mutations: 
    setMyObject(state, payload) 
      state.myObject = shallowRef(payload.value);
    ,

这将跟踪替换对象,但不跟踪对象属性的更改。

为了完整起见,这里是shallowRef的文档:

https://v3.vuejs.org/api/refs-api.html#shallowref

【讨论】:

以上是关于限制 vue/vuex 反应性的主要内容,如果未能解决你的问题,请参考以下文章

Vue、Vuex 和 Firestore:如何在前端显示检索到的 Firestore 数据并使其具有响应性?

限制反应中的多项选择

如何在反应中限制经常重新渲染的组件

限制反应组件中的子组件

Discord收集器反应过滤器设置最大限制的方式

反应表动态页面大小,但有大小限制和分页