Комментарии 21
Пост для тех, кто не читал документацию?
Раньше, я тоже тянул state на прямую из Vuex. Но сейчас, столкнулся с проблемой — формат данных часто меняется, как следствие меняется и формат state в Vuex, и в результате приходится вносить изменения в десятки компонент, которые пользовались этим state (которые надо ещё найти в проекте) делать это приходится в десятках файлов, и это не удобно.
По этому сейчас стараюсь все данные в компоненту загружать только через gettеr, что бы потом, в случае изменения формата данных, внести изменения только в соответствующий getter, тем самым не меняя интерфейс через который компонента взаимодействует с хранилищем. Поначалу такие геттеры могут показаться излишними, но в перспективе, при постоянном изменении формата данных, мне кажется они себя оправдывают…
Ситуация, описанная вами, кажется мне дикой. Вы пытаетесь исправить ошибки проектирования локальным кодом и это рано или поздно приведет к цугундеру. Ну и, кмк, вышеописанное является отличным опусом на тему "Как не нужно использовать геттеры".
DataProvider
— связующее звено между базой данных и приложением — там я и занимаюсь нормализацией.Вы не правы.
Если формат данных меняется, то надо приводить данные к нужному формату еще на входе.
Например API возвращал объект в одном формате:
let user = {
"name": "Вася",
"sex": "male",
"image": "https://site.ru/img/vasya.png"
};
А потом стал возвращать в другом:
let rawUser = {
"login": "Вася",
"male": true,
"avatar": "/img/vasya.png"
};
То не надо пихать его в хранилище в таком виде в каком он пришел. Надо сразу же в обработчике результата запроса или в экшене или, на худой конец, в мутации привести объект к такому формату с которым ваше приложение привыкло работать:
let user = {
"name": rawUser.login,
"sex": rawUser.male ? "male" : "female",
"image": "https://site.ru" + rawUser.avatar
};
И только после этого сохранять его в состоянии.
А откладывать преобразование формата на самый последний момент (в геттер) — это по прежнему не правильно. Геттеры нужны только для того чтобы вычислять производное состояние на основе состояния хранилища. Т. е. это практически то же самое, что computed в компонентах.
Если я неправильно понял и имелось ввиду, то о чем вы говорите то дополню:
Если после изменения структуры хранилища те данные которые были не производными — стали производными, то надо добавить геттер для них и это не будет противоречить тому что написано в статье. Но не стоит добавлять геттеры для всех данных "по умолчанию".
На самом деле все просто. Эта и множество других, похожих как две капли воды, статей про геттеры Vuex пытаются донести одно простое правило:
Не надо делать такие геттеры:
getProp(state) {
return state.prop;
}
А вообще смахивает на перевод статьи с медиума годовой давности.
Приложение перегружается тем, что это увеличивает объем кода. И на связность это никак не влияет, потому что такие геттеры попросту отдают состояние. В общем геттер вида:
getUser(state) {
return state.user;
}
Это просто лишний кусок кода и лишний вызов функции при работе приложения. Не то чтобы он сильно нагружает приложение, но и ценности никакой не имеет.
1)
// модуль
const getters = {
...
getUser: state => state.user
...
}
// компонент
...mapGetters({
user: 'module/getUser',
// другие геттеры
})
2)
{
...mapState({
user: state => state.module.user,
}),
...mapGetters({
// другие геттеры
...
})
}
Как видно, увеличение объема кода в случае 1 будет только в случае, когда другие геттеры не используются и надо писать mapGetters. Во всех остальных ситуациях же кода больше будет как раз таки в случае 2 из-за необходимости использовать и mapState, и mapGetters. Ну в целом по объему кода разницы большой нет, и это не критичный момент во всей этой ситуации на мой взгляд.
И на связность это никак не влияет, потому что такие геттеры попросту отдают состояние
Геттеры попросту отдают состояния и при использовании их извне мне не надо думать про структура стейта, а вот как раз через mapState мне каждый раз приходится задавать путь в стейте, т.е. компонент начинает что-то знать о структуре данных в модуле -> это и есть увеличение связности.
Понятно, что ничего критичного в этой увеличенной связности нет, но все таки не могу согласиться с односторонним называнием «неправильным» подхода с использованием только геттеров. Лично я предпочитаю общение со стейтом только через геттеры, потому что: a) не надо думать о структуре стейта в компоненте б) не надо использовать и mapState и mapGetters в) при изменении структуры не надо думать, есть ли какие-то места в приложении, обращающиеся к стейту напрямую, я всегда знаю что доступ к нему будет осуществляться только через заданный мной интерфейс — геттеры. Пробовал оба подхода и второй оставляет ощущение большей «чёткости».
return this.$store.state.user
происходит что-то типа
return userGetter.call(this.$store.state)
UPD. Однако, я вижу проблему не в этом, а именно в «нецелевом» использовании getter'ов.
Насчет увеличения объема кода...
Вы немного лукавите приводя примеры кода.
Я имею ввиду что если использовать геттеры для каждого куска состояния, то код увеличивается, прежде всего, в хранилище. И с этим уж никто не поспорит, так как это очевидно и я даже пример приводить не буду.
Но в ваших примерах вы приводите также код из компонента. Давайте рассмотрим несколько вариантов:
Вариант 1:
Допустим я не использую mapState и mapGetters. Тогда объем кода в компоненте для меня не меняется так как я в любом случае создаю вычисляемое свойство и в нем одной строкой возвращаю состояние хоть из стейта, хоть из геттера:
// Без геттера users()
users() {
return this.$store.state.users;
},
// С геттером users()
users() {
return this.$store.getters.users;
},
Тут объем кода одинаков (на самом деле нет — не используя геттер мы сэкономим 2 символа, но это мелочи).
Вариант 2:
Допустим я использую mapState и mapGetters.
// Без геттера users()
...mapState([
'users'
]),
// С геттером users()
...mapGetters([
'users'
]),
Снова не вижу разницы.
Тут вы наверное заметите что я вас обманул, так как не использую и mapState, и mapGetters одновременно. Что ж — вы правы, если использовать их вместе, то код в компоненте увеличится:
// Без геттера users()
...mapState([
'users'
]),
...mapGetters([
'articles',
'comments'
]),
// С геттером users()
...mapGetters([
'users',
'articles',
'comments'
]),
Но должен сказать что эти 2 лишние строчки скорее всего окупятся теми строками, которые вы не напишите в хранилище. И еще по своему опыту могу сказать следующее: так как я не пишу геттеры для каждого свойства в состоянии, то мне очень (очень очень) редко приходится использовать в компоненте mapState и mapGetters одновременно. А значит и в последнем случае — я не получу выгоды от вашего подхода.
Насчет увеличения связности...
При использовании mapState, вам надо знать о структуре данных в модуле ровно столько же, сколько и при использовании mapGetters. Если я не прав — приведите пример в котором это не так.
<li v-for="(book, index) in this.$store.state.books.list" :key="index">{{book.title}}</li>
Гетеры нужны для другого, для случая когда нам нужны разные виды представления одного и того же состояния.
Vuex — чрезмерное использование геттеров в приложении. Разбор ошибки