Pull to refresh

Vuex – решаем старый спор новыми методами

Reading time3 min
Views8.9K

Во Vuex есть одна популярная практика — не использовать mapState и mapMutations. Вообще. Вместо это мы сразу же при создании нового значения в store делаем для него геттер, а для каждой мутации – экшен.


Что-то наподобие:


export default new Vuex.Store({
  state: {
    // 1
    count: 0
  },
  getters: {
    // 1
    count: (state) => state.count
  },
  mutations: {
    // 2
    increment(state) {
      state.count++;
    }
  },
  actions: {
    // 2
    increment({ commit }) {
      commit('increment');
    }
  }
});

Холивара не избежать. Это уже доказала вот эта статья. Опять в интернете кто-то не прав, и я должен доказать почему. И я докажу.


Почему нужно писать геттер на каждое значение в state, а на каждую мутацию – писать экшен?


На это есть две причины. Первая: мы упрощаем компоненты и убираем неразбериху.


import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';

export default {
  computed: {
    ...mapState(['count']),
    ...mapGetters(['text'])
  },
  methods: {
    ...mapMutations(['increment']),
    ...mapActions(['fetch'])
  }
};

Представьте, что этот компонент в разы больше. Как вы будете определять какие методы из store — это мутации, а какие экшены? Постоянно лазить в store.js, чтобы проверить? Может, на бумажке запишите? :)


Можно ещё naming convention придумать, типа incrementMutation, decrementAction. Вроде проблему решили, но… как бы, вы сами понимаете. Мы и так два метода пишем, где по-хорошему должен быть один, так ещё и имена у нас супер длинные. Не говоря уже о том, что в <template> они выглядят отвратительно.


Намного удобнее сразу определить для всех значений из state геттеры, а для всех мутаций – экшены. И вызывать в компонентах только их.


Есть ещё и вторая причина, более концептуальная.


Какой самый первый принцип написания кода изучает любой программист? DRY — Don't repeat yourself. То есть не повторяйся. Почему? Потому что, если придется потом что-то менять, менять придется в нескольких местах. И чем больше приложение, тем сложнее сделать какое-либо изменение.


А теперь представим, что при добавлении новой функциональности нам пришлось поменять просто получение значения из state на геттер или поменять мутацию на экшен. Нам придется проходиться по всем компонентам, которые используют эти значения и менять mapState на mapGetters, mapMutations на mapActions и т.д. И боже упаси, если в каком-то компоненте до этого какой-то из методов не использовался. Придется его импортировать, добавлять в компонент и т.д. и т.п.


Короче, причин для нашего решения достаточно.


Однако, есть одна "небольшая" проблема — наш store теперь выглядит как свалка, где каждое новое значение в лучшем случае удваивает количество кода.


С этим:


  1. можно смириться;
  2. можно распихать все по модулям, скрыв проблему (что равносильно тому, чтобы открытый канализационный люк прикрывать картонкой);
  3. можно поступить более радикально и отказаться от vuex в пользу другого способа хранения данных (у Composition API, например, уже есть интересное решение) и уже разбираться с проблемами других библиотек.

У меня есть четвёртое решение. Если вы до сих пор не поняли, что эта статья — реклама, то готовьтесь: сейчас будет рекламная интеграция.


Благо рекламирую я open source. А точнее аддон, который я недавно написал (аддоном я его называю, чтобы не его путали с плагинами).


Называл я его vuex-map. Чтобы и понятно было, и писать import ... вам много не пришлось.


Что он делает? Предоставляет вам два новых метода mapData и mapMethods (названия обсуждаются, вы можете на них повлиять тут).


mapData заменяет функционал mapState и mapGetters. Вы пишете mapData(["count"]), если такой геттер есть, он запускает его, если нет, ищет такое значение в state.


Аналогично, mapMethods возвращает экшен, а если такого экшена нет, комитит мутацию с таким названием.


На самом деле, это очень простая идея. Удивительно, что разрабочики Vuex сразу не добавили её в ядро библиотеки. Благо занимает она всего строчек 100-150, не больше (это, если что, очень мало). А при этом решает все вышеперечисленные проблемы.


Вам абсолютно безразлично, если ли геттер для какого-либо свойства или нет. Если есть, то сработает он, если нет — просто получите значение. Вы в любом случае пишете mapData и не паритесь. То же самое с мутациями и экшенами и mapMethods.


При этом никаких лишних геттеров типа count: state => state.count и т.п. у вас нет.


Что насчёт DRY? Тут тоже всё понятно. Если вам вдруг понадобился геттер или экшен, вы просто его добавляете, в компонентах ничего менять не надо. Новые геттеры и экшены просто "затемнят" значения и мутации с тем же названием.


В каком состоянии находится аддон? Ну с момента, как я его дописал, прошло около суток, поэтому судите сами. :) Тесты на подходе, если вы читаете эту статью, значит где-то там я уже пишу тесты. Тесты написаны.



Предлагаю всем попробовать и оценить. Буду благодарен за любые отзывы и любой вклад. Спасибо.


Если вам так же, как и мне, нравится данная функциональность, и вы хотите видеть её в ядре библиотеки vuex, переходите по данной ссылке, там проходит обсуждение по этой теме.
Tags:
Hubs:
+9
Comments18

Articles