【问题标题】:Efficiently working with large data sets in Vue applications with Vuex使用 Vuex 在 Vue 应用程序中高效处理大型数据集
【发布时间】:2019-03-11 07:38:24
【问题描述】:

在我的 Vue 应用程序中,我有 Vuex 存储模块,其中包含大量处于其状态的资源对象。为了轻松访问这些数组中的单个资源,我制作了 Vuex getter 函数,将资源或资源列表映射到各种键(例如“id”或“tags”)。这会导致性能下降和巨大的内存占用。如何在没有大量重复数据的情况下获得相同的功能和反应性?

存储模块示例

export default {
  state: () => ({
    all: [
      { id: 1, tags: ['tag1', 'tag2'] },
      ...
    ],
    ...
  }),

  ...

  getters: {
    byId: (state) => {
      return state.all.reduce((map, item) => {
        map[item.id] = item
        return map
      }, {})
    },

    byTag: (state) => {
      return state.all.reduce((map, item, index) => {
        for (let i = 0; i < item.tags.length; i++) {
          map[item.tags[i]] = map[item.tags[i]] || []
          map[item.tags[i]].push(item)
        }
        return map
      }, {})
    },
  }
}

组件示例

export default {
  ...,

  data () {
    return {
      itemId: 1
    }
  },

  computed: {
    item () {
      return this.$store.getters['path/to/byId'][this.itemId]
    },

    relatedItems () {
      return this.item && this.item.tags.length
        ? this.$store.getters['path/to/byTag'][this.item.tags[0]]
        : []
    }
  }
}

【问题讨论】:

    标签: javascript vue.js vuejs2 vuex


    【解决方案1】:

    虽然仅存储选择字段是一个很好的中间选项(根据@aidangarza),但当您最终获得非常庞大的数据集时,它仍然不可行。例如。积极处理“仅 2 个字段”的 200 万条记录仍会占用您的内存并破坏浏览器性能。

    一般来说,在 Vue(使用 VueX)中处理大型(或不可预测的)数据集时,只需完全跳过获取和提交机制。继续使用 VueX 来集中你的 CRUD 操作(通过 Actions),但不要试图“缓存”结果,而是让每个组件缓存他们需要的东西,只要他们正在使用它(例如结果的当前工作页面,其一些投影等)。

    根据我的经验,VueX 缓存适用于合理有界的数据,或当前使用上下文中的有界数据子集(即当前登录的用户)。当您拥有不知道其规模的数据时,您的 Vue 组件仅通过 Actions 在“按需”的基础上对其进行访问;对于那些潜在的巨大数据集,没有 getter 或突变。

    【讨论】:

      【解决方案2】:

      虽然我同意@aidangarza,但我认为您最大的问题是反应性。特别是 computed 属性。这会增加很多臃肿的逻辑和缓慢的代码来监听所有内容——这是你不需要的。

      查找相关项目将总是引导您遍历整个列表 - 没有简单的方法可以绕过它。但是如果你自己调用它会快得多。

      我的意思是,computed 属性是关于将要computed 的东西。您实际上是在过滤结果。在你的变量上放置一个观察者,然后自己调用 getter。大致上的东西(半代码):

      watch: {
        itemId() {
          this.item = this.$store.getters['path/to/byId'][this.itemId]
        }
      }
      

      您可以先使用item 进行测试,如果效果更好(我相信它会)- 为更复杂的标签添加观察器。

      祝你好运!

      【讨论】:

      • 计算属性本质上是观察者的快捷方式;所以,这完全一样。
      • 观察者对特定的事物很严格,而计算者决定动态观察什么(可能是相当多的属性,有时它们无法正确拾取);)
      【解决方案3】:

      要解决这个问题,请参考编程中一种古老的标准做法:索引。您可以将映射存储到 state.all 中的项目索引,而不是存储在 getter 中重复的完整项目值的映射。然后,您可以创建一个新的 getter,它返回一个访问单个项目的函数。根据我的经验,索引 getter 函数总是比旧的 getter 函数运行得更快,而且它们的输出占用的内存空间要少得多(在我的应用中平均减少 80%)。

      新建商店模块示例

      export default {
        state: () => ({
          all: [
            { id: 1, tags: ['tag1', 'tag2'] },
            ...
          ],
          ...
        }),
      
        ...
      
        getters: {
          indexById: (state) => {
            return state.all.reduce((map, item, index) => {
              // Store the `index` instead of the `item`
              map[item.id] = index
              return map
            }, {})
          },
      
          byId: (state, getters) => (id) => {
            return state.all[getters.indexById[id]]
          },
      
          indexByTags: (state) => {
            return state.all.reduce((map, item, index) => {
              for (let i = 0; i < item.tags.length; i++) {
                map[item.tags[i]] = map[item.tags[i]] || []
                // Again, store the `index` not the `item`
                map[item.tags[i]].push(index)
              }
              return map
            }, {})
          },
      
          byTag: (state, getters) => (tag) => {
            return (getters.indexByTags[tag] || []).map(index => state.all[index])
          }
        }
      }
      

      新组件示例

      export default {
        ...,
      
        data () {
          return {
            itemId: 1
          }
        },
      
        computed: {
          item () {
            return this.$store.getters['path/to/byId'](this.itemId)
          },
      
          relatedItems () {
            return this.item && this.item.tags.length
              ? this.$store.getters['path/to/byTag'](this.item.tags[0])
              : []
          }
        }
      }
      

      变化看起来很小,但在性能和内存效率方面却有很大的不同。它仍然是完全反应式的,就像以前一样,但您不再复制内存中的所有资源对象。在我的实现中,我抽象出了各种索引方法和索引扩展方法,使代码非常易于维护。

      您可以在 github 上查看完整的概念证明:https://github.com/aidangarza/vuex-indexed-getters

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-30
        • 2015-02-07
        • 1970-01-01
        • 1970-01-01
        • 2012-09-22
        • 1970-01-01
        相关资源
        最近更新 更多