Наш досвід міграції на Vue 3. Частина перша

💡 Усі статті, обговорення, новини про Front-end — в одному місці. Приєднуйтесь до Front-end спільноти!

Вітаю! Мене звати Данііл, і я є Front-end Team Lead в українській продуктовій компанії Webitel. У двох статтях я хочу поділитися нашим досвідом міграції з Vue 2 на Vue 3.

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

За пʼять років розвитку нової версії платформи ми побудували масштабну фронтенд-частину продукту з десятьма різноплановими клієнтськими застосунками: від доволі «типових» CRUD-реєстрів до конструктора візуальних діаграм та власної бібліотеки компонентів.

Наш фронтенд побудований на екосистемі Vue. Починали розробку ми з Vue 2, бандлером Webpack v4 і тест-раннером Jest, з яких ми з часом мігрували на Vue 3, Vite та Vitest.

Спойлер: ні про одну з цих міграцій — не шкодуємо :)

Що цікавого у Vue 3

То чому ми вирішили мігрувати? Розберемо докладніше всі оновлення.

Composition API

Composition API — це новий стиль написання Vue-компонентів. Найцікавіша і найсуттєвіша фіча нової версії Vue.

Наведу список її фічей та переваг.

  1. Перевикористання коду.

Перше і найважливіше: код, написаний з використанням Composition API — простий та зручний у перевикористанні. Це і є його перевагою над Options API.

Перевикористання коду відбувається шляхом так званих Composable-функцій: чогось подібного на React custom hooks, але з важливими для Vue відмінностями (на відміну від React custom hooks, Composable-функції викликаються лише один раз).

  1. Розв’язання проблем, повʼязаних з міксинами.

Також, в цьому контексті дуже важливо порівняти composables з міксинами, за допомогою яких перевикористовується код компонентів у Options API. Річ у тім, що міксини мають свої підводні камені: проблеми, які є непомітні у невеликих проєктах, проте дуже відчутні у великих. Коли ми використовуємо міксини у компоненті, то додаємо властивості, яких не бачимо у самому компоненті. Через це, якщо ми хочемо дослідити його поведінку, нам необхідно провалюватись у міксин, і шукати, що ж там і звідки береться.

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

З власного досвіду розповім, що у нас в одному з проєктів, «старішому», вкладеність міксинів досягала, у найгіршому випадку, п’яти рівнів (тобто, міксин, в міксині, в міксин і так далі). Розбиратися в цьому коді і важко, і страшно. Це просто пекло міксинів. З Composable-функціями такої проблеми немає, тому що кожне використання — прозоре. Адже ми самі обираємо, що хочемо «взяти» з того, що нам повертає функція, і самі обираємо, як його назвати.

  1. Гнучкіша організація коду.

Через відсутність конкретної структури, як в Options API, написання компонентів через Composition API більше нагадує звичайний скрипт: ми можемо довільно розміщати порядок ініціалізації, викликів тощо.

Це є одночасно і плюсом, і мінусом. Адже, з одного боку, дозволяє групувати код за тим, що він робить, а не за тим, чим він є («data» окремо, «computed» окремо тощо) — що є доволі логічним. З іншого боку, забирає каркас структури компонента Options API, а, як відомо, там де більше свободи, там і більше ризиків «а чи зрозуміло все написано і структуровано?».

  1. Глибше розуміння роботи з Vue.

Я ніде не зустрічав такого формулювання плюса Composition API. У мене особисто склалось враження, що він відчувається «ближче» до ванільного JavaScript, і має менше Vue-обгортання між тим, що ми пишемо, і тим що, попадає в бандл. Або принаймні це виглядає явно: наприклад, ми самі маємо імпортувати ref, щоб оголосити якусь змінну реактивною, замість простого «щоб змінна була реактивною, треба написати її у data».

На мою думку, це допомагає краще розуміти, що ти робиш, і зменшує відстань від «я знаю Vue» до «я знаю JavaScript».

  1. Ковток свіжого повітря.

Виключно власне враження: після чотирьох років роботи з Options API можливість писати на Composition API відчувається як ковток свіжого повітря, що стимулює цікавість до чогось нового. А через це просто-таки стає цікавіше писати та створювати щось нове.

  1. TypeScript.

Ми використовуємо JavaScript, тому тут я просто довірюсь документації Vue, яка каже, що Composition API дружить з типізаціями набагато краще, ніж Options API.

Але, було б неправильно не розповісти й про підводні камені Composition API.

  1. Складність.

На мою думку, компонент на Composition API складніший для написання. Треба звикнути до певних нюансів роботи з таким стилем, і кілька разів добре побігати по граблях, щоби писати «складні» компоненти.

  1. Поєднання Options і Composition API.

Ми тут говоримо про міграцію, тож я зроблю припущення, що у вас вже є ціла купа компонентів, написаних на Options API (це не страшно), та велика кількість міксинів до них (а ось це вже страшно).

Міксини доволі важко поєднати з composables в одному компоненті: треба або двічі імпортувати одне і те саме через недоступність компонента this (Options API) у функції setup (Composition API), як-от, скажімо, мапити Vuex. Або треба видумувати якісь обхідні шляхи.

Нам доводилось якісь компоненти просто не чіпати, бо вони на міксинах, або для простої дії мапити, наприклад, роутер та стор окремо.

Нові можливості Vue 3

Також можна згадати інші нові фічі Vue 3, які, хоча й зовсім не виділяються на фоні Composition API, проте, теж є доволі значними.

З найприємнішого я б виділив:

  • Fragments. Нарешті не потрібно обгортати розмітку компонента, яка має кілька root-елементів, у зайвий div! (якщо він справді зайвий, звісно);
  • Emit declaration. Тепер можна прозоро оголошувати еміти компонента, схоже на опис пропсів;
  • provide/inject. Замість кастомних глобальних Vue-змінних тепер можна це робити у певному скоупі та явно(!);
  • краща підтримка TypeScript. Vue 3 повністю написаний на TypeScript. На скільки я розумію, це класне покращення для TypeScript-проєктів — але ми використовуємо JavaScript, тож тут поділитись власним досвідом я не зможу.

Підтримка актуального стека технологій

Сюди можна внести багато речей, починаючи з Vue 2 End of Life до застарівання кодової бази, та навіть developer experience. На мою думку, якщо проєкт «живе» і розвивається, а не доживає і знаходиться на підтримці — підтримка свіжості стека технологій є необхідною і важливою частиною розвитку.

Додам, що застарівання самого фреймворку — це пів біди. Нові проблеми починаються тоді, коли ти намагаєшся знайти свіжі та підтримувані бібліотеки, які працюють зі старою версією фреймворка. Деяких «вічних» бібліотек це, звісно, не стосується, але як тільки ви почнете шукати щось специфічне: скажімо, селект, календарик або карусельку — мій нещодавній досвід шукати щось для на Vue 2 — не з приємних :)

Як мотивувати необхідність міграції для не-розробника

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

Немає конкретної причини, чому міграцію треба проводити терміново (принаймні, я її не бачу) — особливо зважаючи на час та ресурси, витрачені на неї (особисто у мене, з compat-білдом, пішло на це близько місяця — а деякі баги, спричинені переходом, ми ловили ще майже рік).

Але, застарілий стек технологій в якийсь момент стане для вас токсичним: ви будете обмежені у використанні нових бібліотек, оновленнях (зокрема Security-патчах), а ваш проєкт перестане бути привабливим, і за деякий час перетвориться у те, з чим просто не буде бажання працювати (звісно, тут один крок до ширшої теми про технічний борг та потребу в рефакторингах протягом життя проєкту, хоча й наразі не про це).

Також одразу додам! Ми говоримо про активні проєкти, які розвиваються. Якщо продукт вже на стадії підтримки — це інша ситуація, і, я сумніваюсь, що в такому разі міграція є необхідною.

Час на міграцію

У нашій команді це робив я, і міграція дев’яти застосунків до рівня «запустилося і не видає багів на кожному другому кроці» на compatibility-білді зайняло близько місяця (сюди ж я вкладаю і міграцію на Vue Test Utils v2, і виправлення unit-тестів).

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

По моїх відчуттях, з першим застосунком йшло доволі туго, але потім ви зрозумієте патерн, за яким треба робити зміни, і далі справа вже піде «механічно».

Проте, ви самі прекрасно знаєте це слово: «залежить» :)

Залежить від розміру проєкту, від його застарілості, від кількості часу і ресурсів на це. Можливо, у вас це вийде набагато швидше, та можливо і довше. А тепер до роботи!

Як мігрувати

Отже, ви вирішили мігрувати! Але як це зробити?

Зробити основну частину роботи вам допоможе офіційна документація Vue. Тут я наведу лише чекліст, про який не варто забувати під час основної частини міграції.

А речей-то насправді небагато:

  1. Оновити пакети.
  2. Оновити використання Vue-бібліотек (Vue, Vue Router, Vuex, Vue-i18n, наприклад), з, як правило, new …() на create…().
  3. Оновити конфігурацію бандлера, та, за потреби, додати compatibility build, щоб не переписувати і тестувати всі breaking changes (а потреба, я думаю, буде — ми ж все ж мігруємо, а не починаємо з нуля).
  4. А далі просто запускаєте проєкт і поштучно виправляєте помилки, які викидає вам консоль — звучить найпростіше, але, боюсь, сподобається вам в цьому процесі найменше :)

Фінал

Отже, вітаю вас! Ви виконали основні кроки, ваш проєкт запустився і навіть не валиться. Проте, скоріш за все, все одно будуть проблеми. Неочевидні та специфічні. Саме такі ми відловлювали потім понад рік, а над деякими я сидів по кілька днів. Рука так і тягнулась то до лоба, то до сигарети :)

Саме про ці специфічні та неочевидні кейси, з якими ми стикнулися, я розповім у другій статті. Сподіваюсь, ця стаття була вам корисною та підштовхнула, або навіть надихнула вас на міграцію — якщо раптом ви цього ще не зробили!

А тепер щось середнє між TLDR; та FAQ

Q: Довго мігрували?

A: Близько місяця мого фултайм-ресурсу разом з Vue Test Utils v2 і виправленням тестів.

Q: А compatibility build використовували?

A: Звісно! Без нього це було б набагато довше.

Q: Як враження?

A: Рік пройшов, політ нормальний! Враження позитивні, а про міграцію не шкодуємо.

Q: Чому це треба мені як розробнику?

A: Composition API та розв’язання проблем міксинів у великих проєктах, а також актуалізація технічного стека, а отже і доступ до нових версій бібліотек та оновлень.

Q: Чому це треба власнику, менеджеру?

A: Якщо це «живий» проєкт що розвивається, то, на мою думку, застарілі технології є технічним боргом, який з часом стане токсичним і звʼязуватиме вам рухи з пошуком бібліотек, нових рішень, та розв’язання проблем. А отже сповільнить швидкість розробки та якісь результати.

Q: Чи були проблеми після міграції?

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

Q: А Vue 2 хіба вже старий?

A: Не смертельно, але є таке: того року був End of life, а оновлення тулзів і бібліотек — вже, як правило, на Vue 3.

Q: Ми мігрували, ура! Чи треба переписувати компоненти з Options API на Composition API?

A: На мою думку, тільки якщо є потреба. Наприклад, потреба розгрести пекло міксинів.

Q: Composition API vs Options API?

A: Я б казав (а я це піддивився в офіційний Vue документації) Options API — для невеликих проєктів. Composition API — для середніх і більших.

Q: Міксини vs composable функції?

A: Міксини — все. У Vue 3 вони лише для сумісності. Рекомендований спосіб — composables.

👍ПодобаєтьсяСподобалось11
До обраногоВ обраному4
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Пощастило, що не було Vuetify 2 котрий також потрібно було б мігрувати на Vuetify 3

Я так розумію, початково апка була написана на Vue 2 + Webpack? При міграції на Vue 3 не мігрували на vite + vitest? Чому?

Вибачте, перечитав — таки мігрували) Чи були суттєві складнощі з міграцією на vite?

щодо складнощів, намагаюсь пригадати щось прям таке що кошмарило кілька днів, було морочним — але не виходить
якщо для вас це дуже актуальне питання, я можу спробувати пошукати в історії пулл реквестів ту міграцію, і піддивитись: може там справді щось нетривіальне було, про що я забув

взагалі, під час міграції склалося враження, що vite набагато (!) простіший і приємніший за вебпак — навіть під час міграції)

мігрували, але поступово)
на vite+vitest пару місяців тому, а на vue 3 — рік і пару місяців тому :)
все одразу було би дуже-дуже страшно мігрувати)

проблем з vue+webpack не виникало, на скільки пригадую

Дякую за статью, цікаво та розгорунто написано

Єдине що додав би, це те що при міграції я би рекомендував би відразу переходити з vuex на pinia, особливо в випадку якщо виконуєте міграцію Nuxt2 на Nuxt3

Provide/inject як на мою думку можна повністью замінити на Composable-функції але звісно все залежить від ситуації

Також з приводу TypeScript я би сказав що при переході з Vue2 на Vue3 для вас повністю відкриються всі можливості Typescript без необхідності написання складних рішень, як на Vue2 там для цього потрібно провести купу маніпуляцій, і всеодно все що ви будете писати воно буде не таким зручним та зрозумілим як з Vue3

Ще раз дякую, та чекаю на другу частину!

Дякую вам за ваш фідбек, дуже класно що ви поділились досвідом роботи з TypeScript в цьому контексті — він тут дуже доречний, але у мене, нажаль, його немає :)

щодо Vuex -> Pinia — я з вами згоден, що було би дуже класно — але там, на скільки я знаю, compatibility білдів не придумано, як з Vue, через що ми цю міграцію наразі не змогли провести — бо тоді довелось би саме переписувати :(

Мені як людині, яка не працює на постійній основі з vue не вистачає прикладів коду. Типу було -> стало

Код основної частини міграції, якщо використовувати compatibility build (а ми його використовували) — доволі нецікавий. Як правило, там зміни імпортів, викликів основних ліб, невеликі зміни конфігів — якогось цікавого коду «писали от так, а тепер треба писати от так» — дуже мало. Але про нього як раз і буде друга стаття)

Здорово. Я перевёл всё на Vue3 пару лет назад, после того как он вышел. Все нюансы перехода уже не помню, но баги периодически надо было исправлять в процессе перехода, с create, с mounted/unmounted, и ещё всякая мелочёвка. Основная структура кода осталась прежней, и переход занял около недели. Полезность миксин не сильно высокая, поскольку реактивность не делегируется в инкапсулируемый компонент. Ну а div в шаблонах во многих случаях можно и лучше заменять на template.

Дякую, що поділилися своїм досвідом 🙂

Провайд/інжект є у вью 2 з версії 2.2.0
v2.vuejs.org/v2/api/#provide-inject

о, цікаво!
це якось зовсім мимо мене пройшло. дякую, що доповнили :)

Підписатись на коментарі