Эффективная работа с большими наборами данных в приложениях Vue с Vuex - PullRequest
0 голосов
/ 05 октября 2018

В моем приложении Vue у меня есть модули хранилища Vuex с большими массивами объектов ресурсов в их состоянии.Чтобы легко получить доступ к отдельным ресурсам в этих массивах, я создаю функции получения Vuex, которые отображают ресурсы или списки ресурсов на различные ключи (например, «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]]
        : []
    }
  }
}

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Хотя я согласен с @aidangarza, я думаю, что вашей самой большой проблемой является реактивность.В частности, свойство computed.Это добавляет много раздутой логики и медленный код, который слушает все - что вам не нужно.

Поиск связанных элементов всегда приведет вас к циклическому просмотру всего списка - естьне легко обойти это.НО это будет намного быстрее, если вы вызовете это сами.

Я имею в виду, что вычисленные свойства относятся к чему-то, что будет вычислено .Вы фактически фильтруете свои результаты.Поместите наблюдатель в свои переменные, а затем вызовите получатели самостоятельно.Нечто подобное (полукод):

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

Сначала вы можете протестировать с помощью item и, если оно будет работать лучше (что, я считаю, будет), - добавить наблюдателя для более сложных тегов.

Удачи!

0 голосов
/ 05 октября 2018

Чтобы решить эту проблему, обратитесь к старой стандартной практике программирования: индексирование.Вместо того, чтобы хранить карту с полными значениями элементов, дублированными в геттере, вы можете сохранить карту с индексом элемента в state.all.Затем вы можете создать новый метод получения, который возвращает функцию для доступа к одному элементу.По моему опыту, индексные функции получения всегда работают быстрее, чем старые функции получения, и их вывод занимает намного меньше места в памяти (в среднем на 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...