После частого использования Redux и Vuex я начинаю замечать появление некоторых закономерностей. Вот один, который я заметил в своих приложениях Vuex, и как я извлек его в несколько служебных функций. Ниже приводится действие, которое я пишу слишком часто:
const actions = { fetchApiData (store) { // sets `state.loading` to true. Show a spinner or something. store.commit('API_DATA_PENDING') return axios.get('someExternalService') .then(response => { // sets `state.loading` to false // also sets `state.apiData to response` store.commit('API_DATA_SUCCESS', response.data) }) .catch(error => { // set `state.loading` to false and do something with error store.commit('API_DATA_FAILURE', error) }) } }
Написание этого для каждого вызова ajax было повторяющимся и делало мои действия очень долгими.
Я намеревался реализовать что-то, что дало бы мне следующее:
- Обработка всех трех состояний запроса ajax - успеха, сбоя и ожидания.
- Уметь легко создавать новые действия, используя как можно меньше шаблонов.
- Возможность многократного использования - почти все SPA будут использовать такую функциональность.
Три состояния: успех, неудача и ожидание
Первым шагом является реализация функции, которая будет генерировать следующее для мутации под названием GET_INFO_ASYNC
:
const GET_INFO_ASYNC = { SUCCESS: 'GET_INFO_ASYNC_SUCCESS', FAILURE: 'GET_INFO_ASYNC_FAILURE', PENDING: 'GET_INFO_ASYNC_PENDING', loadingKey: getInfoAsyncPending, dataKey: getInfoAsyncData }
Следующая функция делает это для любого заданного type
. Я также включаю loadingKey
и dataKey
- в состоянии моего магазина статус ожидания будет сохранен в loadingKey
, а ответ - в dataKey
.
// src/mutation-types.js import _ from 'lodash' const createAsyncMutation = (type) => ({ SUCCESS: `${type}_SUCCESS`, FAILURE: `${type}_FAILURE`, PENDING: `${type}_PENDING`, loadingKey: _.camelCase(`${type}_PENDING`), stateKey: _.camelCase(`${type}_DATA`) })
Это позволяет нам создать все три мутации, просто вызвав createAsyncMutation(‘GET_INFO’)
.
Затем еще один метод обработки вызова ajax и мутаций для нас.
// src/async-util.js import axios from 'axios' const doAsync = (store, { url, mutationTypes }) => { store.commit(mutationTypes.PENDING) axios(url) .then(response => { store.commit(mutationTypes.SUCCESS, response.data) }) .catch(error => { store.commit(mutationTypes.FAILURE) }) } export default doAsync
Мы просто передаем магазин, URL-адрес службы, к которой мы обращаемся, и mutation-types
, созданный с помощью createAsyncMutation
. Это решает проблему шаблонного действия. Мы можем использовать указанную выше утилиту для создания таких действий:
const actions = { getInfoAsync(store) { doAsync(store, { url: 'https://jsonplaceholder.typicode.com/posts/1', mutationTypes: types.GET_INFO_ASYNC }) }, } }
Последнее, что нужно сделать, это создать мутации:
const mutations = { [types.GET_INFO_ASYNC.SUCCESS] (state, data) { state[types.GET_INFO_ASYNC.loadingKey] = false Vue.set(state, [types.GET_INFO_ASYNC.dataKey], data) }, [types.GET_INFO_ASYNC.PENDING] (state) { Vue.set(state, types.GET_INFO_ASYNC.loadingKey, true) } }
Это обрабатывает loadingKey
, а также устанавливает данные. Обратите внимание, что мы используем Vue.set
, простое выполнение state[types.GET_INFO_ASYNC.loadingKey]
не будет работать с системой реактивности Vue - свойства должны быть созданы при начальной загрузке или Vue.set
, чтобы они были реактивными.
Все еще существует небольшой шаблон, связанный с вышеупомянутыми мутациями. Однако я иногда обрабатываю данные в мутациях - относится ли это к действиям или нет, зависит от ряда факторов. Примером может быть toggleTodo
mutation, где вы находите todo
, используя полезную нагрузку из внешнего API, а затем переключаете done
на false или что-то в этом роде.
Приведенный выше код может использоваться приложением следующим образом:
// App.vue <template> <div id="app"> <p> Pending: {{ $store.state.getInfoAsyncPending }} </p> <p> {{ $store.state.getInfoAsyncData }} </p> </div> </template> <script> export default { created () { this.$store.dispatch('getInfoAsync') } } </script>
Следующее, что я хотел бы сделать, это извлечь это в плагин, чтобы сделать его еще более гибким и многоразовым. Может следующий пост!
Суть исходного кода здесь. См. Файл main.js
для получения инструкций по настройке демонстрации или отправьте сообщение, могу ли я загрузить репо :)
Мне интересно услышать, как другие люди используют разные шаблоны или методы, чтобы сделать действия более лаконичными и уменьшить шаблон, связанный с ними, поэтому, пожалуйста, оставьте комментарий!