使用Vuex在Vue应用程序中有效处理大型数据集

时间:2018-10-05 18:47:36

标签: javascript vue.js vuejs2 vuex

在我的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]]
        : []
    }
  }
}

3 个答案:

答案 0 :(得分:2)

要解决此问题,请参考编程中的一种古老的标准做法:建立索引。您可以将地图存储到state.all中该项目的索引中,而不是存储具有在getter中重复的完整项目值的地图。然后,您可以创建一个新的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

答案 1 :(得分:1)

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

通常,在Vue中使用VueX处理大型(或不可预测)数据集时,只需完全跳过get和commit机制。继续使用VueX集中化CRUD操作(通过Actions),但不要尝试“缓存”结果,而是让每个组件在使用它们之前一直缓存所需的内容(例如,结果的当前工作页) ,某些投影等等)。

根据我的经验,VueX缓存旨在用于合理限制的数据或当前使用上下文中(即当前登录的用户)的有限数据子集。当您对数据的规模一无所知时,可以仅通过Actions保持Vue组件“根据需要”访问数据;那些潜在的巨大数据集不会发生吸气剂或突变。

答案 2 :(得分:-1)

虽然我同意@aidangarza,但我认为您最大的问题是反应性。特别是computed属性。这就增加了很多of肿的逻辑和慢速的代码,可以监听所有内容,而您则不需要。

查找相关项目将始终会导致您遍历整个列表-绕不上任何简单的方法。但是,如果您自己调用它,它将更快。

我的意思是,计算属性是关于将要计算的内容。您实际上是在过滤结果。将观察者放在变量上,然后自己调用getter。大致内容(半代码):

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

您可以先使用item进行测试,并且是否可以更好地工作(我相信这样做会更好)-为更复杂的标签添加观察者。

祝你好运!