Vuex-models - простой способ научить vuex работать с v-model

О том, как легко и непринуждённо можно создавать реактивные поля в vuex store и столь же просто использовать их в ваших компонентах.

Основная идея в Vuex - ограничение доступа к прямому изменению состояния. Т.е. по хорошему, вы не можете взять store.state и менять в нём значения вручную (можете, без strict, конечно, но, пожалуйста, не делайте так) — вместо этого, вы создаёте action для получения/обработки данных, mutation - для непосредственной записи значения в store. Также, хорошим тоном будет создать getter для получения этих значений.

 

Общая схема работы vuex

 

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

 

Кроме того, если вам нужны реактивные возможности v-model — для каждого свойства придётся писать вычислимые (computed) свойства, у которых геттеры (get) будут получать данные из стейта, а сеттеры (set) — вызывать мутации.

 

В перспективе — это тонны однотипного кода и в описании ваших vuex store, и в computed свойствах компонентов.

 

Я столкнулся с этой проблемой при разработке opencart модуля foc_csv — количество реактивных полей в интерфейсе, даже на начальной стадии, было довольно велико, и в процессе разработки их должно было становиться всё больше.

 

Чтобы как-то автоматизировать рутину с vuex — я создал пару удобных функций, которые затем, в усовершенствованном виде и легли в основу библиотеки vuex-models.

 


 

vuex-models

Библиотека предоставляет всего лишь две функции — genVuexModels и mapVuexModels.

 

genVuexModels — получает на входе список моделей (полей в state), а также, опционально, название переменной в state куда будут сохраняться значения моделей (по умолчанию — vxm).

 

Существует два способа генерации моделей - первый способ использование конфигурационного объекта вида название_свойства:значение_по_умолчанию, является рекомендуемым в большинстве случаев, однако вы можете использовать и просто массив с названиями, но в таком случае вам необходимо самостоятельно создать объект состояния и передать его в стор.

 

/*
  Рекомендуемый способ - добавлен с версии 1.0.2
*/
const models = genVuexModels({
  modelName: 'defaultValue',
  foo: {
    bar: 'baz'
  }
// также, вы можете записывать модели напрямую в state
// если передадите вторым аргументом false, но это не рекомендуется
}, 'featureStateVariable')

/*
  Устаревший способ
*/
const state = { 
  featureStateVariable: {
    modelName: 'defaultValue'
  } 
}
const { mutations, actions, getters } = genVuexModels(['modelName'], 'featureStateVariable')

 

Объект models будет содержать полный набор необходимых свойств — state,mutations,actions,getters, в самом простом случае их можно добавить в ваш store с помощью spread-оператора. 

Вообще можно добавить их прямо в store (если у вас простенькое приложение), но я рекомендую использовать vuex модули для этого.

const featureModule = {
  namespaced: true,
  ...models
}

const store = new Vuex.Store({
  modules: {
    featureNamespace: featureModule
  }
}) 

 

Вот и весь процесс генерации моделей. Серьёзно!

 

Следующий шаг — добавление этих моделей в ваши vue компоненты. Делается это с помощью функции mapVuexModels:

 

import { mapVuexModels } from 'vuex-models'

return {
  computed: {
    ...mapVuexModels({
      model: 'modelName',
      foo: 'foo'
    }, 'featureNamespace') // если вы не используете vuex modules, то не передавайте этот аргумент
  }
}

 

Второй аргумент функции опционален, используйте его в том случае, если ваши модели находятся в namespaced модулях vuex (использование модулей — это рекомендуемый способ).

 

Теперь вы можете использовать свойства model и foo как обычные вычислимые свойства vue, поскольку mapVuexModels генерирует объекты с getter/setter следующего вида:

 

get () { return this.$store.getters['<PATH_TO_MODEL>_<MODEL_NAME>'] }
set (val) { this.$store.dispatch('set<PATH_TO_MODELS>_<MODEL_NAME>', val)

 

Таким образом эти свойства можно спокойно использовать в v-model без риска создания побочных эффектов:

 

<input v-model="foo">

 

Где взять?

 

Библиотека оформлена как npm пакет — https://www.npmjs.com/package/vuex-models

Также, очень приветствуются замечания и pull-реквесты — https://github.com/ikenfin/vuex-models