Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Vue
Code

Як побудувати комплексні, великомасштабні програми Vue.js з Vuex

by
Difficulty:AdvancedLength:LongLanguages:

Ukrainian (українська мова) translation by Pembelight (you can also view the original English article)

Навчитися працювати та використовувати Vue.js так легко, що за допомогою цього фреймворку кожний може створити простий додаток. Використовуючи документацію Vue, навіть новачки можуть виконати таку роботу. Однак, чим далі, тим все стає серйознішим та заплутанішим. Правда полягає в тому, що безліч глибоко вкладених компонентів можуть швидко перетворити ваш додаток на незрозумілу плутанину. 

Основна проблема комплексного застосування полягає в тому, як керувати станом між компонентами без написання довгого коду або створення побічних ефектів. У цьому уроці ви дізнаєтеся, як вирішити таку проблему за допомогою Vuex: бібліотеки керування станом для побудови складних програм Vue.js.

Що таке Vuex?

Vuex - це бібліотека керування станом, спеціально налаштована для створення складних, масштабних програм Vue.js. Вона використовує глобальне, централізоване сховище для всіх компонентів у програмі, користуючись перевагою системи реактивності для миттєвих оновлень.

Магазин Vuex розроблений таким чином, що його стан неможливо змінити з будь-якого компоненту. Це гарантує те, що стан може бути модифікований лише передбачуваним чином.  Таким чином, ваше сховище стає єдиним джерелом істини: щоб запобігти компонентам додатку пошкодити стан, доступ до якого здійснюється іншими компонентами, кожен елемент даних зберігається тільки один раз і тільки для читання.

Чому Вам потрібна Vuex?

Ви можете запитати: Чому мені потрібна бібліотека Vuex? Чи не можу я просто вставити загальний стан в звичайний файл JavaScript та імпортувати його в мій Vue.js додаток?

Звичайно, можна, але в порівнянні з простим глобальним об'єктом, сховище Vuex має ряд істотних переваг та плюсів:

  • Сховище Vuex реактивне. Після того, як компоненти отримують стан зі сховища, вони реактивно оновлюються кожного разу, коли змінюється стан.
  • Компоненти не можуть безпосередньо змінювати стан сховища. Єдиний спосіб змінити стан сховища - навмисне вчинення мутацій. Це гарантує, що кожна зміна стану залишає доступний для відстеження запис, що полегшує налагодження та тестування програми.
  • Ви можете легко налаштувати ваш додаток завдяки інтеграції Vuex з Vue's DevTools extension.
  • Сховище Vuex надає вам чіткий огляд того, як все підключається та впливає на ваш додаток.
  • Простіше підтримувати та синхронізувати стан між кількома компонентами, навіть якщо змінюється ієрархія компонентів.
  • Vuex робить можливим пряму взаємодію між перехресними компонентами.
  • Якщо компонент зруйнований, стан у сховищі Vuex залишиться недоторканим.

Початок роботи з Vuex

Перш ніж ми почнемо, я хочу освітлити декілька речей.

По-перше, щоб зрозуміти цей урок, потрібно добре розуміти систему Vue.js та її компоненти, або принаймні мати мінімальний досвід роботи з фреймворком.

Крім того, мета цієї статті полягає не в тому, щоб показати вам, як побудувати реальну складну програму; мета полягає в тому, щоб зосередити вашу увагу більше на концепціях Vuex та, яким чином ви можете використовувати їх для побудови складних додатків.  З цієї причини я збираюся використовувати дуже прості та повсякденні приклади без будь-якого надлишкового коду. Як тільки ви повністю зрозумієте концепцію Vuex, ви зможете її застосувати на будь-якому рівні складності.

Насамкінець, я буду використовувати ES2015 синтаксис. Якщо ви з ним не знайомі, ви можете про нього дізнатися тут.

А тепер давайте почнемо!

Налаштування проекту Vuex

Першим кроком до початку роботи з Vuex є встановлення Vue.js і Vuex на вашому комп'ютері. Є кілька способів це зробити, але ми використаємо найпростіший. Просто створіть HTML-файл і додайте необхідні посилання CDN:

Я використав деякі CSS, щоб компоненти виглядали краще, але не беріть до уваги цей CSS код. Він лише допомагає отримати візуальне уявлення про те, що відбувається. Просто скопіюйте та вставте в тег </head>:

Тепер давайте створимо деякі компоненти для роботи. Усередині тегу <script>, що знаходиться над тегом </body>, помістіть наступний код Vue:

Тут ми маємо екземпляр Vue, батьківський компонент і два дочірні компоненти. Кожен компонент має заголовок "Score:", куди ми виведемо стан додатку.

Останнє, що потрібно зробити, це обгорнути в тег <div> з id = "app" відразу після тегу <body>, а потім розмістити батьківський компонент всередині:

Підготовча робота вже закінчилася, і ми готові йти далі.

Вивчення Vuex

Керування станом

У реальних проектах ми стикаємося з труднощами щодо стратегій, як організувати та структурувати контент. Ми групуємо пов'язані речі в різних розділах, категоріях тощо. Це схоже на книжкову бібліотеку, в якій книги класифікуються та розміщуються в різних відділах для швидшого пошуку.  Vuex організовує дані додатку та логіку, пов'язані зі станом у чотирьох групах або категоріях: стан, геттери, мутації і дії.

Стан і мутації є основою для будь-якого сховища Vuex:

  • state - це об'єкт, який утримує стан даних додатку.
  • mutations також є об'єктом, що містить методи, які впливають на стан.

Геттери і дії подібні до логічних проекцій стану і мутацій:

  • getters містять методи, які використовуються для абстрактного доступу до стану, і виконують деякі завдання попередньої обробки, якщо це необхідно (обчислення даних, фільтрація тощо).
  • actions - це методи, що використовуються для запуску мутацій і виконання асинхронного коду.

Давайте розглянемо наступну діаграму, щоб більше прояснити:

Vuex State Management Workflow Diagram

З лівого боку ми маємо приклад сховища Vuex, який створимо пізніше. З правого боку у нас діаграма робочих процесів Vuex, що показує, як різні елементи Vuex працюють разом і взаємодіють між собою.

Щоб змінити стан, конкретний компонент Vue повинен здійснити мутації (наприклад, this.$store.commit ('increment', 3)), а потім ці мутації змінюють стан (score отримує 3). Після цього геттери автоматично оновлюються завдяки реактивній системі Vue, і вони оновлюються в перегляді компонента (з this.$store.getters.score).

Мутації не можуть виконувати асинхронний код, оскільки це унеможливлює запис і відстеження змін в дебаг інструментах, таких як Vue DevTools. Щоб використовувати асинхронну логіку, потрібно привести її в дію. У цьому випадку компонент буде спочатку відправляти дії (this.$store.dispatch('incrementScore', 3000)), де виконується асинхронний код, і тоді ці дії зроблять мутації, які мутують стан.

Створіть основу Vuex Store

Тепер, коли ми дізналися, як працює Vuex, давайте створимо основу для нашого сховища Vuex. Встановіть наступний код над реєстрацією компонента ChildB:

Щоб забезпечити з кожного компонента глобальний доступ до сховища Vuex, нам потрібно додати store властивість в приклад Vue:

Тепер ми можемо отримати доступ до сховища з кожного компонента з такою змінною this.$store.

Якщо ви відкриєте проект з CodePen в браузері, ви побачите наступний результат.

App Skeleton

Властивості стану

Об'єкт стану у вашому додатку містить усі спільні дані. Звичайно, при необхідності кожен компонент також може мати власний приватний стан.

Уявіть, що ви хочете побудувати ігровий додаток, і вам потрібна змінна для зберігання рахунку гри. Отже, ви ставите його в об'єкт стану:

Тепер ви можете отримати безпосередній доступ до оцінки стану. Повернемося до компонентів і повторно використаємо дані зі сховища.  Щоб мати можливість повторно використовувати реактивні дані зі сховища стану, слід використовувати обчислювані властивості. Отже, давайте створимо у батьківському компоненті обчислювану властивість score():

У шаблоні батьківського компонента вкажіть вираз {{score}}:

А тепер зробіть те ж саме для двох дочірніх компонентів.

Vuex настільки розумний, що він зробить всю роботу за нас, реактивно оновлюючи score властивості при зміні стану. Спробуйте змінити значення score і подивитися, як оновлюється результат у всіх трьох компонентах.

Створення геттерів

Звичайно, це добре, коли всередині вище показаних компонентів дозволяється повторно використовувати ключове слово this.$store.state. Але уявіть такий розвиток події:

  1. У широкомасштабному додатку, де кілька компонентів отримують доступ до стану магазину за допомогою this.$store.state.score, ви вирішили змінити назву score. Це означає, що ви повинні змінити назву змінної всередині кожного компонента, який її використовує!
  2. Ви хочете використовувати обчислене значення стану. Наприклад, скажімо, ви хочете надати гравцям бонус у 10 балів, коли рахунок досягне 100 балів. Отже, коли рахунок досягає 100 балів, додається 10 бонусних балів. Це означає, що кожен компонент повинен містити функцію, яка повторно використовує score і збільшує її на 10. Ви матимете повторний код у кожному компоненті, що не дуже практично!

На щастя, для вирішення таких ситуацій Vuex пропонує рішення, які дійсно спрацьовують. Уявіть централізований геттер, який звертається до стану сховища і забезпечує функцію геттера кожному елементу стану. Якщо необхідно, цей геттер може застосувати деякі обчислення до елемента стану. А якщо потрібно змінити назви деяких властивостей стану, ви змінюєте їх лише в одному місці, в цьому геттері.

Створимо геттер score():

Геттер отримує state як перший аргумент, а потім використовує його для доступу до властивостей стану.

Примітка: геттери також отримують getters в якості другого аргументу. Ви можете використовувати його для доступу до інших геттерів у сховищі.

У всіх компонентах змініть обчислювану властивість score(), щоб використовувати геттер score()  замість score стану безпосередньо.

Тепер, якщо ви вирішили змінити score на result, вам потрібно оновити його лише в одному місці: у геттері score(). Спробуйте в цьому CodePen!

Створення мутацій

Мутації є єдиним допустимим способом зміни стану. Тригерні зміни просто означають втілення мутацій в компонентних методах.

Мутація є значною мірою функцією обробника подій, яка визначається за ім'ям. Функції обробника мутацій отримують state як перший аргумент. Можна також передати додатковий другий аргумент, який називається payload для мутації.

Створимо мутацію increment():

Мутації не викликаються безпосередньо! Для виконання мутації слід викликати метод commit() з ім'ям відповідної мутації і можливими додатковими параметрами. Це може бути лише одна мутація, подібно step як в нашому випадку, або декілька, загорнуті в об'єкт.

Використовуємо мутацію increment() у двох дочірніх компонентах, створивши метод з назвою changeScore():

Ми безпосередньо здійснюємо мутацію замість того, щоб змінювати this.$store.state.score, тому що ми хочемо чітко відстежувати зміни, внесені мутацією. Таким чином, ми зробимо логіку нашої програми більш прозорою, відслідкованою та легко розсудливою. Крім того, він дає можливість використати інструменти, такі як Vue DevTools або Vuetron, які можуть реєструвати всі мутації, візуалізувати стани та виконувати налаштування в часі.

Тепер давайте використаємо метод changeScore(). У кожному шаблоні двох дочірніх компонентів створіть кнопку та додайте до неї обробник подій:

При натисканні кнопки стан збільшується на 3, і ця зміна відображатиметься у всіх компонентах. Тепер ми ефективно досягли прямого перехресного компонентного зв'язку, що неможливо із вбудованим механізмом Vue.js в стилі "менше властивостей, більше подій". Перевірте це в нашому прикладі на CodePen.

Створення дій

Дія - це лише функція, яка здійснює мутацію. Вона змінює стан побічно, що дозволяє виконувати асинхронні операції.

Створимо дію incrementScore():

Дії отримують перший параметр context, який містить всі методи і властивості зі сховища. Зазвичай ми просто вилучаємо необхідні частини, використовуючи аргумент ES2015, який деструктурує.  Часто стає в нагоді метод commit. Дії, як і мутації, також отримують другий аргумент payload.

У компоненті ChildB змініть метод changeScore():

Щоб викликати дію, ми використовуємо метод dispatch() з назвою відповідної дії і додатковими параметрами, все так само як і з мутаціями.

Тепер кнопка Change Score з компонента ChildA збільшить рахунок на 3. Ідентична кнопка з компонента ChildB зробить те ж саме, але після затримки в 3 секунди.  У першому випадку ми виконуємо синхронний код і використовуємо мутацію, але у другому випадку виконуємо асинхронний код, і замість нього потрібно використовувати дію. Подивіться, як це працює в нашому прикладі CodePen.

Vuex хелпери

Vuex пропонує кілька корисних хелперів (помічників), які можуть спростити процес створення стану, геттерів, мутацій і дій. Замість того, щоб писати ці функції вручну, ми можемо наказати Vuex створити їх для нас. Давайте подивимося, як це працює.

Замість того, щоб написати score() обчислюваної властивості, як це:

Ми просто використовуємо хелпер mapState() :

Властивість score() створюється для нас автоматично.

Те ж саме відноситься і до геттерів, мутацій і дій.

Щоб створити геттер score(), скористаємося хелпером mapGetters():

Щоб створити метод changeScore(), скористаємося хелпером mapMutations():

Коли використовується для мутацій і дій з аргументом payload, ми повинні передати цей аргумент в шаблон, де визначається обробник події:

Якщо ми хочемо, щоб changeScore() використовував дію замість мутації, ми використовуємо mapActions():

Знову ж таки, ми повинні визначити затримку в обробнику подій:

Примітка: Усі помічники повертають об'єкт. Отже, якщо ми хочемо використовувати їх у комбінації з іншими локальними обчислюваними властивостями або методами, нам потрібно об'єднати їх в один об'єкт. На щастя, з оператором розповсюдження об'єкта (...) ми можемо зробити це без використання будь-яких засобів.

У нашому CodePen, ви побачите приклад того, як всі хелпери використовуються на практиці.

Створення сховища More Modular

Здається, що проблема зі складністю постійно ставить на нашому шляху перешкоди. Ми вирішили цю проблему раніше, створивши сховище Vuex, в якому ми спростили управління станом та взаємодію між компонентами.  У цьому сховищі, зібрано все в одному місці, а відтак легко маніпулювати та програмувати.

Однак, коли наш додаток ускладнюється, цей простий у керуванні файл сховища стає все більшим і, як наслідок, його важче підтримувати.  Знову ж таки, для поліпшення структури прикладних програм нам потрібні деякі стратегії та методи з простим способом підтримки. У цьому розділі ми розглянемо кілька методів, які можуть нам у цьому допомогти.

Використання модулів Vuex

Vuex дозволяє розділити об'єкт зберігання на окремі модулі. Кожен модуль може містити власний стан, мутації, дії, геттери та інші вкладені модулі. Після створення необхідних модулів ми реєструємо їх у сховищі.

Подивимося на дії:

У наведеному вище прикладі ми створили два модулі, один для кожного дочірнього компонента. Модулі - це просто об'єкти, які ми зареєструватися як scoreBoard і resultBoard в об'єкті modules всередині сховища. Код для childA такий же, як і в сховищі з попередніх прикладів. У коді для childB ми додаємо деякі зміни у значення та назву.

Давайте тепер налаштуємо компонент ChildB, щоб відобразити зміни в модулі resultBoard.

У компоненті ChildA єдине, що нам потрібно змінити, це метод changeScore():

Як ви бачите, розкладання сховища на модулі робить його набагато легшим і коригованим, при чому зберігається чудова функціональність. Зацініть оновлений CodePen, щоб побачити його в дії.

Модулі простору імен

Якщо хочеться або потрібно в модулях використовувати одну й ту ж назву для певної властивості або методу, то вам слід розглянути назви. В іншому випадку ви можете спостерігати деякі дивні побічні ефекти, такі як виконання всіх дій з однаковими назвами або отримання неправильних значень стану.

Для простору назв модуля Vuex, ви просто встановлюєте namespaced властивість у true.

У наведеному вище прикладі ми зробили назви властивостей і методів однаковими для двох модулів. А тепер ми можемо використовувати властивість або метод з префіксом назви модуля. Наприклад, якщо ми хочемо використати геттер get() з модуля resultBoard, набираємо його так: resultBoard/score. Якщо ми хочемо, щоб score() отримувався з модуля scoreBoard, тоді ми вводимо його так: scoreBoard/score.

Давайте тепер змодифікуємо наші компоненти так, щоб відобразити внесені нами зміни.

Як ви бачите з нашого прикладу на CodePen, тепер ми можемо використовувати потрібний метод або властивість і отримувати очікуваний результат.

Розкладання Vuex Store на окремі файли

У попередньому розділі ми до деякої міри покращили структуру додатку, розділивши сховище на модулі. Ми зробили сховище чистішим та організованішим, але код сховища та його модулі все ще знаходяться в одному великому файлі.

Таким чином, наступний логічний крок полягає в тому, щоб розділити сховище Vuex на окремі файли. Суть полягає у тому, щоб мати окремий файл для самого сховища та один для кожного з його об'єктів, включаючи модулі.  Це означає наявність окремих файлів для стану, геттерів, мутацій, дій і для кожного окремого модуля (store.js, state.js, getters.js і т.д.). Ви можете побачити приклад цієї структури наприкінці наступного розділу.

Використання компонентів Vue Single File

Ми зробили Vuex сховище настільки модульним, настільки це можливо. Наступне, що ми можна зробити, це застосувати таку ж стратегію до компонентів Vue.js. Ми можемо помістити кожен компонент в окремий автономний файл з розширенням .vue. Щоб дізнатися, як це працює, відвідайте сторінку документації Vue Single File Components.

Отже, у нашому випадку ми матимемо три файли: Parent.vue, ChildA.vue і ChildB.vue.

Нарешті, якщо ми об'єднаємо всі три методики, ми отримаємо таку або подібну структуру:

У нашому курсі на репозиторії GitHub ви побачите завершений проект із зазначеною структурою.

Щоб запам'ятати

Давайте повторимо деякі основні моменти, які потрібно пам'ятати про Vuex:

Vuex - це бібліотека керування сховищем, яка допомагає створювати складні, масштабні додатки. Вона використовує глобальне, централізоване сховище для всіх компонентів програми. Для абстрактного стану ми використовуємо геттери. Геттери дуже схожі на обчислювані властивості та є ідеальним рішенням для фільтрування або обчислювання.

Магазин Vuex реактивний, і компоненти не можуть безпосередньо змінювати стан сховища. Єдиний спосіб змінbти стан - це вчинення мутацій, які є синхронними операціями. Кожна мутація повинна виконувати тільки одну дію, вона має бути якомога простішою, і нести відповідальність тільки за оновлення деякої частини стану.

Асинхронну логіку слід інкапсулювати в дії. Кожна дія може здійснювати одну або більше мутацій, і одна мутація може бути здійснена більш ніж однією дією. Дії можуть бути складними, але вони ніколи не змінюють стан безпосереднім шляхом.

Нарешті, модульність є ключем до підтримки. Щоб розв'язати складність і зробити наш код модульним, ми використовуємо принцип "поділити та завоювати" та техніку розкладання коду.

Висновок

Ось і все! Ви вже знаєте основні концепції Vuex, і тепер ви готові почати їх застосовувати на практиці.

Для стислості та простоти я навмисно пропустив деякі деталі та особливості Vuex, тому вам треба прочитати повну документацію Vuex, щоб дізнатися все про Vuex та його набір функцій.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.