Всем привет! Нам бы хотелось представить вашему вниманию статью Энтони Гора о миграции Vue.js-приложения на Vuex.
Вид приложения, над которым будет вестись работа по миграции
Далее следует перевод статьи. Всех, кому интересна данная тема, приглашаю под кат.
Одной из сложностей в освоении Vuex является то, что это не столько библиотека, сколько паттерн проектирования. Из этого следует, что работа с Vuex – не столько работа с API, сколько структурирование вашего кода в соответствии с паттерном. Если вы новичок во Vuex, это может отпугивать.
В этой статье я продемонстрирую как начать миграцию имеющегося Vue.js приложения на Vuex. Я покажу вам, как идентифицировать те части вашего приложения, которые должны принадлежать Vuex, и те, которые не следует переносить, как превратить функции вашей компоненты в мутации, действия и так далее, и, наконец, мы обсудим, какие преимущества мы получим в результате.
Если вы не уверены, следует ли вам использовать Vuex, я рекомендую вам сначала прочесть вот эту статью: “Что за Vuex: Руководство для новичков по хранилищу данных во Vue”
Учебный пример: Vue.js-кинотеатр
В качестве учебного примера мы перенесем демонстрационное приложение Vue.js-кинотеатр на Vuex. Это приложение, состоящее из одного файла, построенное на компонентах Vue, вполне хороший кандидат для демонстрации миграции на Vuex, так как в нем содержится значительное количество состояний.
Если хотите узнать, как построить Vue.js-кинотеатр с нуля, это часть моего полного курса разработки на Vue.js.
Помните, что целью Vuex является управление состоянием приложения. Из этого следует, что для того, чтобы мигрировать Vue.js-приложение на Vuex, мы должны определить, каково будет его состояние. Мы скоро увидим состояние в коде, но полезно сначала осознать, каким оно будет, посредством простого изучения того, что вообще делает приложение, например, в нашем случае, оно отображает список фильмов, который может быть отфильтрован по дню, времени сеанса или жанру.
Какие данные приложения должны принадлежать Vuex?
Используя Vue Devtools, или же просто инспектор кода, мы можем увидеть данные принадлежащие каждому vue-компоненту:
Наша задача не переносить все данные во Vuex-стор (центральное хранилище данных). Вместо этого мы хотим определить данные, которые:
- Изменяются на протяжении жизненного цикла приложения (статические данные не требуют особого менеджмента).
- Разделены между несколькими объектами/компонентами.
Это то, о чем мы говорим, когда произносим состояние приложения, и это именно то, что мы хотим поместить в хранилище.
Например, взгляните на эту группу компонентов check-filter
, кастомных чекбоксов для фильтрации фильмов:
Каждый check-filter
компонент имеет свойство checked
, а также текст, который передаётся ему в качестве свойства, в частности Before 6pm, After 6pm и т.д.:
src/components/CheckFilter.vue
<template>...</template>
<script>
export default {
data() {
return {
checked: false
}
},
props: [ 'title' ],
...
}
</script>
Свойство checked
отвечает за состояние каждого чекбокса в приложении, а значит принадлежит состоянию приложения в целом, так как может изменятся на протяжении жизненного цикла приложения, и другие компоненты должны реагировать соответствующим образом на изменения состояния этих чекбоксов.
Свойство title
, с другой стороны, не изменяется и никак не влияет на другие компоненты. Поэтому нам нет нужды управлять ими с помощью Vuex-стора.
Примечание: если у вас есть какое-либо локальное состояние, которое вы хотели бы отслеживать с помощью Vue DevTools, тогда это нормально, если вы добавите его в стор; это не является нарушением паттерна.
Свойcтва → Хранилище (Стор)
Если вы спроектировали свое приложение с помощью компонент, как это сделал я с Vue.js-кинотеатром, тогда первым местом, где нужно искать данные, которые вы бы хотели поместить в стор, были бы свойства компонент. Особенно те свойства, посредством которых передаются одинаковые данные в разные компоненты.
К примеру, я передаю свойство day
из корневого компонента приложения в day-select
и movie-list
компоненты. C помощью компонента day-select пользователь выбирает день, для которого он хочет посмотреть фильмы, и список фильмов фильтруется в соответствии с выбранным значением:
Компонент day-select
Чтобы переместить day
во Vuex мы можем просто удалить его из корневого компонента и переместить в объект state хранилища Vuex:
export default new Vuex.store({
state: {
day: moment() // the initial value
},
...
});
Теперь мы можем удалить привязку из темплейта v-bind:day="day"
, а в компоненте заменить стандартное свойство на computed-свойство, которое обращается к нашему хранилищу (стору). В случае с компонентом day-select
это будет следующий код:
export default {
props: [ 'day' ],
...
}
который преобразуется в
export default {
computed: {
day() {
return this.$store.state.day
}
}
}
События → Мутации
Тогда как свойства – это данные, которые передаются в компонент, то события влияют на состояние этих данных. Слушатели событий в вашем коде должны быть преобразованы во Vuex-мутации. Функции, вызывающие события, будут переделаны, и станут вызывать мутации, а не события.
Вернемся к нашему примеру с компонентом day-select
. Когда пользователь изменит день, кликнув на соответствующий элемент интерфейса, вызывается следующий метод компонента:
selectDay(day) {
this.$emit('setDay', day);
}
Корневой компонент содержит слушатель события, который будет реагировать на кастомное событие:
created() {
this.$on('setDay', day => { this.day = day; });
}
Теперь функция-слушатель может быть перенесена из корневого компонента в объект мутаций нашего хранилища с небольшими изменениями:
src/store/index.js
export default new Vuex.Store({
state: {
day: moment()
},
mutations: {
setDay(state, day) {
state.day = day;
}
}
Далее нужно заменить вызов события в компоненте на вызов мутации нашего хранилища:
selectDay(day) {
this.$store.commit('setDay', day);
}
Наконец, мы также можем удалить слушатель события из темплейта, т.е. код v-on:click="setDay"
.
AJAX → Actions (действия)
Действия во Vuex – это механизм обработки ассинхронных мутаций. Если вы используете AJAX, чтобы обновлять состояние своего приложения, возможно вам потребуется обернуть его в действие (action).
Во Vue.js-кинотеатре я использую AJAX (с помощью Vue Resource HTTP client), чтобы получать данные из API моего сервера:
src/main.js
created() {
this.$http.get('/api').then(response => {
this.movies = response.data;
});
}
Это случается только единожды на протяжении жизненного цикла приложения, когда оно только создается, поэтому это действие кажется слишком тривиальным, чтобы использовать Vuex в данном случае, но в этом нет никакого вреда, при этом мы получим преимущество в отладке нашего приложения с помощью Vuex.
Действия вызываются из приложения и должны в свою очередь вызывать мутацию в центральном хранилище, когда асинхронное действие завершено. Вот код, который это реализует:
src/store/index.js
export default new Vuex.Store({
state: {
...
movies: []
},
mutations: {
...
setMovies(state, movies) {
state.movies = movies;
}
},
actions: {
getMovies({ commit }) {
Vue.http.get('/api').then(response => {
commit('setMovies', response.data);
});
}
}
});
Метод created
теперь должен только лишь вызывать действие:
src/main.js
created() {
this.$store.dispatch('getMovies');
}
Результаты и преимущества
Теперь, когда мы мигрировали Vue.js-кинотеатр на Vuex, какие же мы все-таки получили преимущества? Возможно, для конечного пользователя не будет заметно никаких улучшений в скорости загрузки страниц, производительности и так далее, но это значительно упростит жизнь разработчикам.
Отладка
Теперь, когда наше приложение управляется через Vuex, мы легко можем увидеть любые изменения в нем с помощью Vue Devtools:
Vuex и Vue Devtools не только логируют изменения в хранилище, но и позволяют вам «отмотать» изменения, так что вы можете пошагово проследить, каким образом они воздействуют на приложение.
Как сказал создатель библиотеки Эван Вью: «Преимущество Vuex в том, что изменения в нем являются отслеживаемыми, воспроизводимыми и восстанавливаемыми».
Разделение компонентов и состояния
Во Vue.js-кинотеатре мы отслеживаем фильтры в двух массивах, один из которых содержит данные о времени сеанса, другой – жанр фильма. Добавление и удаление фильтров ранее обрабатывалось функцией в корневом компоненте. Используя Vuex мы можем перенести эту логику в мутацию в нашем хранилище:
src/store/index.js
mutations: {
...
checkFilter(state, { category, title, checked }) {
if (checked) {
state[category].push(title);
} else {
let index = state[category].indexOf(title);
if (index > -1) {
state[category].splice(index, 1);
}
}
}
},
Преимущество этого разделения в том, что код таким образом становится более читабельным, логичным и поддерживаемым.
Сокращение компонент
Без такого паттерна хранилища как Vuex мы передаем данные компонентам в виде свойств и событий, которые должны быть объявлены в HTML-шаблоне. В больших приложениях из-за этого шаблоны могут становится достаточно многословными.
Управление данными во Vuex позволяет этот код:
<movie-list
:genre="genre"
:time="time"
:movies="movies"
:day="day"
></movie-list>
преобразовать в этот:
<movie-list></movie-list>
Понравилась статья? Подпишитесь на рассылку и получайте самые последние статьи по Vue.js на почту.
Автор: zyardim