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

aid*_*rza 7 javascript vue.js vuex vuejs2

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

存储模块示例

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
      }, {})
    },
  }
}
Run Code Online (Sandbox Code Playgroud)

组件示例

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]]
        : []
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

aid*_*rza 5

要解决此问题,请参考编程中一种古老的标准做法:索引。您可以将映射存储到state.all. 然后,您可以创建一个新的 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])
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

新组件示例

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])
        : []
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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