Ограничить vue/vuex реактивность
Давайте предположим, что у нас есть некоторый массив объектов, и эти объекты никогда не меняются. Например, это могут быть результаты поиска, полученные от Google Maps PlaceApi - каждый результат представляет собой довольно сложный объект с идентификатором, названием, адресом, координатами, фотографиями и множеством других свойств и методов.
Мы хотим использовать vue / vuex для отображения результатов поиска на карте. Если какие-то новые результаты отправляются в магазин, мы хотим нарисовать их маркеры на карте. Если какой-либо результат будет удален, мы хотим удалить его маркер. Но внутренне каждый результат никогда не меняется.
Есть ли способ указать vue отслеживать массив (push, splice и т. Д.), Но не углубляться и не отслеживать какие-либо свойства его элемента?
Пока я могу себе представить только некрасивое разделение данных - сохранить массив идентификаторов в vue и иметь отдельный кэш-идентификатор вне хранилища. Я ищу более элегантное решение (например, knockout.js observableArray).
4 ответа
Ты можешь использовать Object.freeze()
на этих объектах. Это приводит к (действительно крошечному!) Снижению производительности, но оно должно быть незначительным, если вы не добавляете сотни или тысячи объектов одновременно.
edit: в качестве альтернативы, вы могли бы заморозить массив (намного лучшая производительность), что заставит Vue пропустить "повторную активацию" его содержимого.
И когда вам нужно добавить объекты в этот массив, создайте новый, чтобы заменить старый:
state.searchResults = Object.freeze(state.searchResults.concat([item]))
Это было бы довольно дешево даже для больших массивов.
На первый взгляд, разделение данных кажется не таким уж уродливым решением для этой задачи. Все, что нам нужно, это использовать геттеры вместо необработанного состояния 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
}
Я создал вилку vue под названием vue-for-babylonians, чтобы ограничить реактивность и даже позволить некоторым свойствам объекта быть реактивными. Посмотрите здесь.
С его помощью вы можете указать Vue, чтобы объекты, хранящиеся в vue или vuex, не становились реактивными. Вы также можете указать Vue, чтобы определенное подмножество свойств объекта стало реактивным. Вы обнаружите, что производительность значительно улучшилась, и вам понравится удобство хранения и передачи больших объектов, как обычно в vue/vuex.
Вы можете использовать для этого.
Сначала импортируйте это:
import {shallowRef} from 'vue';
В ваших мутациях может быть такая мутация:
mutations: {
setMyObject(state, payload) {
state.myObject = shallowRef(payload.value);
},
}
Это будет отслеживать замену объекта, но не изменения свойств объекта.
Для полноты здесь представлена документация к
shallowRef
: