Как стать автором
Обновить

Комментарии 21

Улучшение "на лицо" или "налицо".

Пост для тех, кто не читал документацию?

Интересная статья, тоже задавался этим вопросом. Но сейчас я всё же предпочитаю использовать getter в компонентах…
Раньше, я тоже тянул state на прямую из Vuex. Но сейчас, столкнулся с проблемой — формат данных часто меняется, как следствие меняется и формат state в Vuex, и в результате приходится вносить изменения в десятки компонент, которые пользовались этим state (которые надо ещё найти в проекте) делать это приходится в десятках файлов, и это не удобно.
По этому сейчас стараюсь все данные в компоненту загружать только через gettеr, что бы потом, в случае изменения формата данных, внести изменения только в соответствующий getter, тем самым не меняя интерфейс через который компонента взаимодействует с хранилищем. Поначалу такие геттеры могут показаться излишними, но в перспективе, при постоянном изменении формата данных, мне кажется они себя оправдывают…

Ситуация, описанная вами, кажется мне дикой. Вы пытаетесь исправить ошибки проектирования локальным кодом и это рано или поздно приведет к цугундеру. Ну и, кмк, вышеописанное является отличным опусом на тему "Как не нужно использовать геттеры".

В моём понимании, правила взаимодействия между компонетами и храниличем это тоже важная часть архитектуры. В нашем случае роль представления — вьюхи, играют компоненты Vue, а роль хранилича данных это state Vuex. Из архитектурных патерно MV*, мы знаем, что прямое обращение к храниличу данных из уровня представления — это не лучшая идея( как раз по причине их возможного изменения), лучше обращаться к ним через какую нибудь прослойку — например модель. Хоть vuex реализует flux, но проблема, как показывает опыт, никуда не дивается, прослойка нужна, и я пытаюсь обеспечить её через getter, снижая тем самым связанность прилоежния. А как вы архитектурно решаете эту проблему?
Вы верно излагаете теорию, но на практике я вижу ошибку в том, что хранилище хранит не модели (если я верно все понял). XAHTEP26 ниже все очень хорошо расписал, на мой взгляд. Я для решения описанной вами проблемы ввожу в приложение понятие 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 в компонентах.

Думаю речь шла не про изменение формата данных из API, а про периодическую необходимость менять структуру стейта какого-то модуля (что-то добавилось, что-то решили больше внутрь убрать).

Если я неправильно понял и имелось ввиду, то о чем вы говорите то дополню:


Если после изменения структуры хранилища те данные которые были не производными — стали производными, то надо добавить геттер для них и это не будет противоречить тому что написано в статье. Но не стоит добавлять геттеры для всех данных "по умолчанию".


На самом деле все просто. Эта и множество других, похожих как две капли воды, статей про геттеры 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>

Гетеры нужны для другого, для случая когда нам нужны разные виды представления одного и того же состояния.
<li v-for="(book, index) in this.$store.state.books.list" :key=«index»>{{book.title}}


Пожалуйста, нет, не надо так
Аргументы?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории