【问题标题】:Restrict vue/vuex reactivity限制 vue/vuex 反应性
【发布时间】: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)。

【问题讨论】:

    标签: javascript vue.js vuejs2 vuex


    【解决方案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

        【讨论】:

          猜你喜欢
          • 2018-11-29
          • 1970-01-01
          • 2018-10-01
          • 2021-07-08
          • 2019-09-08
          • 1970-01-01
          • 2021-10-23
          • 1970-01-01
          • 2018-10-12
          相关资源
          最近更新 更多