У меня возникла проблема с обновлением данных в хранилище Vuex в слегка вложенной структуре данных.
Мой полный проект немного сложнее, чем то, что показано ниже, но проблема, с которой я сталкиваюсь сейчас, упрощена и настроена так, что для каждого страница проекта будет целевая страница, на которой размещен видимость таблицы, а также отдельный табличный компонент, внутри которого есть элемент Vuetify v-data-table, который просто принимает заголовки и элементы в качестве свойств и отображает их. Его можно представить в следующем виде:
- Project Page
|- Landing page
|- Table Visibility
|- Table Component
|- v-data-table
Один из способов думать об этом заключается в следующем: Для каждого животного (страница проекта) есть три разные породы/типа, каждая со своими характеристиками. Таким образом, вышеупомянутая структура станет:
- Dog
|- Landing page
|- Table Visibility
|- German Shepherd
|- v-data-table
|- Bull Terrier
|- v-data-table
|- Labrador Retriever
|- v-data-table
- Cat
|- Landing page
|- Table Visibility
|- Russian Blue
|- v-data-table
|- British Shorthair
|- v-data-table
|- Persian
|- v-data-table
- Bird
|- Landing page
|- Table Visibility
|- Cockatiel
|- v-data-table
|- Parrot
|- v-data-table
|- Macaw
|- v-data-table
Когда пользователь переходит на любую из страниц проекта, он или она будет иметь возможность выбрать с помощью компонента видимость таблицы, какие таблицы будут доступны для просмотра. Этот компонент выглядит примерно так:
<template>
<div>
<v-card>
<v-card-title>
<p class = "title ma-0">Table Visibility</p>
</v-card-title>
<v-divider class = "mx-5"></v-divider>
<v-card-text>
<v-layout row wrap fill-height>
<v-checkbox
v-for = "(title, idx) in tableTitles"
v-model = "tableVisibility"
:label = "title"
:value = "title"
:key = "idx"
class = "mx-1"
multiple
></v-checkbox>
</v-layout>
</v-card-text>
<v-card-actions>
<v-switch
v-model = "showAll"
:label = "showAll ? 'Hide All' : 'Show All'"
></v-switch>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: "TableChoices",
props: ['tableTitles'],
data() {
return {
showAll: false,
displayTables: [],
}
},
methods: {
},
computed: {
...mapState({
pageName: state => state.pageName,
}),
},
watch: {
showAll(bool) {
bool ?
this.displayTables = this.tableTitles :
this.displayTables = []
},
displayTables: {
handler() {
let tableObj = {};
this.tableTitles.forEach(title => { tableObj[title] = this.displayTables.indexOf(title) > -1 })
this.$store.commit('setTableVisibility', {page: this.pageName, tables: tableObj})
if (this.displayTables.length === this.tableTitles.length) {
this.showAll = true
} else if (this.displayTables.length === 0) {
this.showAll = false
}
}
},
}
}
</script>
<style scoped>
</style>
<template>
<div>
</div>
</template>
<script>
import Dog from '@/components/Dog'
import Cat from '@/components/Cat'
import Bird from '@/components/Bird'
import { mapState, mapGetters } from 'vuex'
export default {
name: "LandingPage",
components: {
Dog,
Cat,
Bird,
},
data() {
return {
items: {},
headers: {},
}
},
computed: {
...mapState({
pageName: state => state.pageName,
}),
...mapGetters({
tableVisibility: 'getTableVisibility'
})
},
watch: {
tableVisibility: {
handler() { console.info('tableVisibility in LandingPage.vue', this.tableVisibility)},
deep: true
},
}
}
</script>
<style scoped>
</style>
Нет смысла размещать компоненты Dog, Cat или Bird, потому что единственное, что они содержат, — это таблица данных Vuetify, поэтому они могут просто использовать заполнители, поскольку они не важны.
Магазин устроен так:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const debug = process.env.NODE_ENV !== 'production'
function initialState() {
return {
pageItems: {},
pageHeaders: {},
pageName: '',
tableTitles: {
Dog: ['German Shepherd', 'Bull Terrier', 'Labrador Retriever'],
Cat: ['Russian Blue', 'British Shorthair', 'Persian'],
Bird: ['Cockatiel', 'Parrot', 'Macaw']
},
tableVisibility: {
Dog: {
German Shepherd: false,
Bull Terrier: false,
Labrador Retriever: false
},
Cat: {
Russian Blue: false,
British Shorthair: false,
Persian: false
},
Bird: {
Cockatiel: false,
Parrot: false,
Macaw: false
}
}
}
}
const state = {...initialState()}
const mutations = {
setTableVisbility(state, payload) {
const page = payload.page;
const tables = payload.tables;
// Vue.set(state.tableVisibility, page, tables)
state.tableVisibility[page] = Object.assign({}, tables);
}
}
const getters = {
getTableVisibility: (state) => ( state.tableVisibility[state.PageName] ),
getCurrentPageTableTitles: (state) => ( state.tableTitles[state.pageName] ),
}
export default new Vuex.Store({state, mutations, getters, strict: debug})
Как я уже сказал, весь проект сложнее (функция магазина, которую я показываю здесь, на самом деле находится в модуле и т. д.), но у меня возникают проблемы даже с получением обновленного значения tableVisibility в компоненте LandingPage.vue. Я пробую несколько разных методов применения обработчика глубокий к наблюдателю (https://vuejs.org/v2/api/#watch и https://medium.com/@stijlbreuk/six-random-issues-and-their-solutions-in-vuejs-b16d470a6462); Я попытался разделить функциональность массива в хранилище, очистив массив с помощью соединения (Как очистить массив в JavaScript?), а затем вернув все элементы обратно в массив; Я попытался использовать метод массива фильтр, чтобы попытаться создать новый массив; Я попытался сохранить tableVisibility как объект (с именем таблицы в качестве ключа и значением как true или false), чтобы я мог использовать Object.assign (https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Предостережения); Я нашел эту страницу о нормализации данных (https://forum.vuejs.org/t/vuex-best-practices-for-complex-objects/10143/2), но кажется, что это не так уж сложно до такой степени, что это уже довольно просто.
Я на пределе своих возможностей, пытаясь понять, что происходит, и я был бы очень признателен за помощь.
Я обновил проект, чтобы включить геттеры, и реактивность работает во всех других компонентах, кроме значения tableVisibility. Я попытался оставить элементы tableVisibility в виде массивов, затем попытался преобразовать их в объекты, как показано в этом редактировании, и попытался использовать Vue.set для обновления значений, что не сработало, затем попытался использовать Object.assign как показано в этой текущей версии. Если я уйду со страницы, а затем вернусь, значения обновятся, просто они, кажется, теряют реактивность, несмотря на мои попытки использовать Vue.set и Object.assign.
Это просто опечатка, которую я забыл исправить при переносе кода из моего проекта в этот более простой. Редактирую сейчас



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Мое предложение для вас, чтобы начать думать о нормализации ваших данных. Некоторые большие проблемы:
tableVisibility.Во-первых, напишите методы получения и методы мутации; сделайте их достаточно универсальными, чтобы вы могли повторно использовать их где угодно в компонентах Vue, и заставить все ваши компоненты Vue использовать их вместо прямого доступа к состоянию
Пример:
const getters = {
getCurrentActivePage: (state) => {}, // return pageName
getProjects: (state) => {},
getProjectById: (state) => (projectId) => {},
getTablesOfProjects: (state) => (projectId) => {},
getTable: (state) => (tableID) => {},
isTableVisible: (state) => (tableID) => {}
}
const mutations = {
setTableVisibility: (state, { tableID, newValue }) => {}
}
После отделения логики пользовательского интерфейса от структуры состояния можно приступить к нормализации данных состояния. Попробуйте представить свое состояние как небольшую базу данных и спроектировать ее логически. Один из способов сделать это так:
const state = {
pageName: '',
projects: {
'123': { id: '123', name: 'Dog' },
'234': { id: '234', name: 'Cat' }
// ...
},
tables: {
'123z': { id: '123z', projectId: '123', name: 'German Shepherd', visible: false }
}
}
где идентификатор может быть автоматически сгенерирован с использованием пакетов npm, таких как nanoid.
Даже без нормализации я все же рекомендую вам сделать шаг 1 (развязка). Если ваш магазин сложный, вы никогда не можете быть уверены, что изменение состояния магазина приведет к поломке компонентов Vue. Good getters и mutations по крайней мере поймают неверные данные и вернут значение по умолчанию для вашего компонента.
Спасибо за помощь. Я обновил проект по вашему предложению (хотя я еще не нормализовал данные), и он все еще вызывает проблемы. Код в исходном сообщении обновлен, и я был бы признателен, если бы вы могли взглянуть еще раз.
Ничего, код как написано выше (github.com/JVP3122/vuex-реактивность) работает. В моем основном проекте должно быть что-то еще.
Vue.set(state.tableVisibility, page, table): стол должен быть столом?