Ограничить реактивность vue / vuex

Предположим, у нас есть некоторый массив объектов, и эти объекты никогда не меняются. Например, это могут быть результаты поиска, полученные с api мест google maps - каждый результат представляет собой довольно сложный объект с идентификатором, заголовком, адресом, координатами, фотографиями и множеством других свойств и методов.

Мы хотим использовать vue / vuex для отображения результатов поиска на карте. Если какие-то новые результаты помещаются в магазин, мы хотим нарисовать их маркеры на карте. Если какой-то результат удален, мы хотим удалить его маркер. Но внутренне каждый результат никогда не меняется.

Есть ли способ указать vue отслеживать массив (push, splice и т. Д.), Но не углубляться и не отслеживать какие-либо свойства его элемента?

На данный момент я могу представить только некрасивое разделение данных - сохраните массив идентификаторов в vue и создайте отдельный кеш-по-идентификатору вне магазина. Я ищу более элегантное решение (например, knockout.js observableArray).


person Kasheftin    schedule 01.06.2017    source источник


Ответы (4)


Вы можете использовать Object.freeze() на этих объектах. Это дает (действительно крошечный!) Удар по производительности, но он должен быть незначительным, если вы не добавляете сотни или тысячи объектов одновременно.

edit: В качестве альтернативы вы можете заморозить массив (гораздо лучшая производительность), что заставит Vue пропустить «повторную активацию» его содержимого.

И когда вам нужно добавить объекты в этот массив, создайте новый, чтобы заменить старый:

state.searchResults = Object.freeze(state.searchResults.concat([item]))

Это было бы довольно дешево даже для массивов большего размера.

person Linus Borg    schedule 01.06.2017
comment
Я сомневаюсь, что если searchResults[ ] находится в состоянии хранилища, и мы используем вычисляемое свойство в компоненте для извлечения searchResults[ ] из хранилища, vue будет просто обновляться, если есть изменение в длине этого массива или даже любое изменение в свойствах элемента. - person Vamsi Krishna; 01.06.2017
comment
В обоих подходах вычисленное свойство будет обновляться при изменении содержимого массива. - person Linus Borg; 01.06.2017
comment
если внутри все результаты не меняются, то вычисляемое свойство будет обновляться только при добавлении или удалении каких-либо элементов в массиве. Тогда зачем делать дополнительный шаг и замораживать объект, если вы знаете, что элементы в массиве не изменены - person Vamsi Krishna; 01.06.2017
comment
Я скорректировал свой ответ альтернативным решением, а именно замораживанием массива и заменой его на freh при добавлении материала (потому что вы не можете push() в замороженный массив). - person Linus Borg; 01.06.2017
comment
да, это было бы лучшим решением. Спасибо, что развеяли мои сомнения - person Vamsi Krishna; 01.06.2017
comment
Как вы думаете, достаточно ли одной заморозки или требуется что-то вроде deepFreeze? - person Kasheftin; 01.06.2017
comment
Одного достаточно. Vue будет пропускать дочерние объекты, когда встречает замороженный объект. - person Linus Borg; 02.06.2017

На первый взгляд разделение данных кажется не таким уж уродливым решением этой задачи. Все, что нам нужно, это использовать геттеры вместо необработанного состояния 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
}
person Kasheftin    schedule 01.06.2017

Я создал вилку vue под названием vue-for-babylonians, чтобы ограничить реактивность и даже позволить некоторым свойствам объекта быть реактивными. Проверьте это здесь.

С его помощью вы можете указать Vue, чтобы объекты, хранящиеся в vue или vuex, не были реактивными. Вы также можете указать Vue, чтобы определенное подмножество свойств объекта стало реактивным. Вы обнаружите, что производительность значительно улучшилась, и вам понравится удобство хранения и передачи больших объектов, как обычно в vue / vuex.

person Coffiend    schedule 04.06.2020

Для этого можно использовать shallowRef.

Сначала импортируйте это:

import {shallowRef} from 'vue';

В ваших мутациях может быть такая мутация:

mutations: {
    setMyObject(state, payload) {
      state.myObject = shallowRef(payload.value);
    },
}

Это будет отслеживать замену объекта, но не изменения свойств объекта.

Для полноты изложения вот документация к shallowRef:

https://v3.vuejs.org/api/refs-api.html#shallowref

person Waruyama    schedule 03.06.2021