Миграция на Vue 3 начинается не с кода: как донести важность и подготовить проект

от автора

«Бизнесу это невыгодно!», «Мы не будем вкладывать в это ресурсы», «Зачем, оно и так отлично работает» — какие аргументы можно привести против всех этих высказываний?

Миграция на Vue 3 по факту это технический долг, на который обычно выделяют по минимуму времени. Проекты работают, пользователи не жалуются, а небо всё также синее. Можно жить дальше?

Можно! Но есть моменты на которые важно обратить внимание и донести бизнесу.

В этой статье хочется поговорить про две важные стороны миграции: почему переход на Vue 3 важен для бизнеса и разработки, а также как подготовить большой legacy-проект к миграции ещё до первого изменения в коде.

Будет полезно: разработчикам, техлидам, руководителям команд и тем, кто отвечает за развитие продукта.

Почему миграция важна

Начнём с причин, почему откладывать миграцию на Vue 3 — не лучшая идея.

  • Безопасность. Самый, на мой взгляд, важный момент, который бизнес не сможет игнорировать. Версия Vue 2 с конца 2023 года больше не поддерживается, а это значит, что больше никаких патчей и обновлений. Любая уязвимость — проблема разработчиков и команды.

  • Устаревшие инструменты. Со временем начинаешь ощущать, что технологии ушли далеко вперёд, а проект стоит на месте. Пишу со стороны разработчика, который испытал это на себе — приходится слушать доклады только ради общего развития, а не для того, чтобы бежать и внедрять себе на проект что-то новенькое. Как пример, тот же самый Vite просто так не затащить на старый проект. А это влияет от скорости сборки до комфорта самих разработчиков. Чем дольше откладываешь, тем больше будет проблем в будущем.

  • Полезности Vue 3, и это не только про новый синтаксис. В новую версию добавили много полезных вещей, исправили реактивность, добавили полезный Composition API, сделали разработку более прогнозируемой и удобной.

  • Оптимизация и скорость сборки. Vue 3 быстрее и легче: размер бандла меньше, более эффективный рендер, доработанный tree‑shaking. Для средних и крупных проектов даёт ощутимый прирост — быстрее запускается, собирается и грузится. Почти для всех проектов важна скорость загрузки и время отклика. И миграция становится уже не техническим долгом, а реальной бизнес‑задачей.

  • Привлекательнось для найма новых сотрудников. Да, с наймом сейчас сложно. Но важно смотреть на перспективу. Замотивированные разработчики стремятся идти в ногу со временем и новыми технологиями. На мой взгляд, это влияет на профессиональную привлекательность сотрудника.

Лично для меня самый важный аргумент в пользу миграции на Vue 3 — безопасность. Со всем можно работать. Но знать, что есть потенциальные дыры и создавать потенциальные риски для пользователей — сомнительная мотивация.

Возможные риски: утечка пользовательских данных, взлом аккаунта, внедрение вредоносного кода через зависимости, репутационные и финансовые потери. И это только верхушка айсберга.

Если перечисленных пунктов не достаточно, чтобы донести важность миграции на Vue 3, то есть некоторые мысли:

Скрытый текст

Предложить менеджерам не идею: «А давайте всё перепишем и заживём!». А конкретную БИЗНЕС задачу, и главное делать это ПОЭТАПНО.

Например, нужно реализовать какую‑нибудь фичу и сразу же закладывать в неё немного рефакторинга под миграцию на Vue 3. В задаче фиксируется как необходимый шаг для того, чтобы новый функционал работал корректно. Мы не проводим миграцию — а делаем бизнес‑улучшения и снижаем технический долг.

Главное — заранее выстроить план миграции: выписать все проблемные места, несовместимые API и изменения, которые предстоит внести. Когда большой объём работы разбит на понятные небольшие шаги, то миграция перестаёт выглядеть чем-то большим и страшным.

Именно здесь начинается вторая важная часть — подготовка к миграции.

Подготовка к миграции на Vue 3

Миграция большого legacy-проекта с Vue 2 на Vue 3 — это в первую очередь про анализ, планирование и организацию процесса. Основная сложность начинается ещё до первого коммита: оценить объём работы, найти несовместимые API, продумать стратегию миграции и минимизировать риски для команды и продукта.

Главная проблема миграции — не новое Vue API. Главная проблема — отсутствие процесса.

Поделюсь своим опытом, где мне пришлось лидировать миграцию: самостоятельно составлять план работ, оценивать время и риски, доносить важность перехода.

С чем пришлось работать:

  • Крупный legacy-проект UI-библиотека, который был написан частично на render-функциях и с повсеместным использованием $children, которые удалены во Vue 3.

  • Мы решили не переходить на Composition API, а оставить Option API. Во-первых, проект будет проще мигрировать на той же структуре, во-вторых, разработчикам меньше времени потребуется на то, чтобы вникнуть в новую версию Vue 3 без доп плюшек в виде Composition API. Не надо намеренно усложнять себе жизнь, а делать всё поэтапно.

  • Два разработчика, сжатые сроки и большие ожидания успешного результата.

С чего начать подготовку

Первый шаг, которым точно не стоит пренебрегать: перевести проект на самую последнюю версию Vue 2 — 2.7.16 с обратной совместимостью. В ней уже есть многие возможности из Vue 3.

Это снижает стоимость будущей миграции и уменьшает технический долг:

  • встроенный Composition API

  • поддержка defineComponent

  • <script setup>

  • улучшенные TypeScript-типы

  • ESM/CJS совместимость ближе к Vue 3

  • обновлённые devtools и tooling expectations

  • более современная сборка через Vite/новые loaders

Важный момент, что на этом этапе мы ещё не переписываем старую функциональность на новую. Но именно с этой версии можно добавлять новые фичи с учётом новых возможностей, которые без проблем потом будут работать на API Vue 3.

И чтобы не накапливать технический долг в будущем стоит обговорить с командой и установить правила как вы будете писать новый код в продакшене, не используя устаревшие возможности Vue 2. Это важный шаг про внедрение новых процессов.

Организация parallel development

Чтобы не замораживать основную разработку и не ломать продакшен мы приняли решение процесс распараллелить: основная разработка продолжалась в developers, а миграция на Vue 3 в отдельной ветки с префиксом VUE3, которая была создана от основной developers.

Новые изменения переносились из developers через cherry-pick, после чего при необходимости сразу адаптировались под Vue 3 на основании таблицы миграции, о которой расскажу дальше.

Такой подход позволил продолжать выпускать бизнес-задачи без feature freeze, а миграцию проводить постепенно и контролируемо.

Аудит несовместимостей

Далее ВАЖНЫЕ шаги подготовки к миграции:

  1. Таблица изменений Vue 2 → Vue 3. Составить таблицу всех изменений, которые предстоит сделать на проекте, которая даст возможность:

    • Оценить предстоящие работы

    • Понять риски

    • На основании этих пунктов можно планировать и создавать задачи на разработку

    Для миграции есть официальная документация. Теперь эта наш главный помощник. В ней можно увидеть все изменения Vue 3 по сравнению с Vue 2.

    Также документация рекомендует установить «the migration build» @vue/compat. Но это последний шаг в подготовке к миграции. А пока нам нужна таблица совместимостей. На данном этапе мы из неё возьмём только список всех изменений. И создадим свою таблицу соответствия старых и новых API. Делюсь с вами наработками ниже.

    Таблица соответствий старых и новых API

    Скрытый текст

    Категория

    Vue 2

    Vue 3

    Что делать / чем заменить

    Создание приложения

    new Vue()

    createApp()

    Заменить на createApp(App).mount(‘#app’)

    Mount

    mount заменял root element

    mount НЕ заменяет container

    Проверить CSS/DOM-зависимости

    Global API

    Vue.extend()

    defineComponent()

    Использовать defineComponent

    Global properties

    Vue.prototype.xxx

    app.config.globalProperties.xxx

    Перенести глобальные свойства

    Reactivity

    Vue.set()

    не нужен

    Использовать обычное присваивание

    Reactivity

    Vue.delete()

    не нужен

    Использовать delete obj.key

    Reactivity

    Vue.observable()

    reactive()

    Использовать reactive

    Lifecycle

    beforeDestroy

    beforeUnmount

    Переименовать hook

    Lifecycle

    destroyed

    unmounted

    Переименовать hook

    Destroy API

    $destroy()

    удалён

    Удалить использование

    Event bus

    $on/$off/$once

    удалены

    Использовать mitt / emitter / store

    Children API

    $children

    удалён

    Использовать refs / props

    Slots

    $scopedSlots

    $slots

    Slots теперь функции

    Listeners

    $listeners

    merged into $attrs

    Использовать $attrs

    Attrs

    $attrs без class/style

    включает class/style

    Учесть при прокидывании attrs

    Data option

    data мог быть object

    data() всегда function

    Всегда использовать function

    Mixins merge

    deep merge

    shallow merge

    Проверить mixins/extensions

    Watch arrays

    mutation trigger

    только deep: true

    Добавить deep watch

    Async components

    () => import()

    defineAsyncComponent()

    Использовать новый API

    Functional SFC

    <template functional>

    удалён

    Использовать function components

    Functional components

    object API

    plain functions

    Переписать functional components

    v-model

    value + input

    modelValue + update:modelValue

    Обновить API компонентов

    .sync

    :prop.sync

    v-model:prop

    Заменить syntax

    .native

    @click.native

    удалён

    Удалить modifier

    Filters

    {{ text | filter }}

    удалены

    Заменить на methods/computed

    Custom directives

    bind/inserted/unbind

    beforeMount/mounted/unmounted

    Переименовать hooks

    Render functions

    createElement(h)

    h()

    Обновить render API

    Transition group

    всегда wrapper element

    wrapper отсутствует

    Проверить layout/styles

    Transition classes

    старые классы

    новые naming rules

    Обновить transition classes

    v-if + v-for

    другой precedence

    v-if выше

    Разделить через template

    <template v-for>

    key на child

    key на template

    Перенести :key

    inline-template

    поддерживался

    удалён

    Удалить

    is=»»

    работал везде

    только <component>

    Использовать <component :is=»»>

    v-bind=»object»

    order insensitive

    order sensitive

    Проверить порядок attrs

    KeyCode modifiers

    .13, .enter

    keyCode removed

    Использовать key names

    Config

    config.keyCodes

    удалён

    Удалить

    Config

    config.productionTip

    удалён

    Удалить

    Config

    config.silent

    удалён

    Удалить

    Custom elements

    ignoredElements

    compilerOptions.isCustomElement

    Обновить config

    Internal utils

    Vue.util

    private

    Удалить использование

    Hook events

    hook:mounted

    удалены

    Переписать логику

    Boolean attrs

    false удалял attr

    attr остаётся

    Проверить bindings

    Enumerated attrs

    special coercion

    убрано

    Проверить attrs

    Compiler filters

    template filters

    удалены

    Переписать

    Ref in v-for

    старое поведение

    новое поведение

    Проверить refs

  2. Аудит использования несовместимых API. После составления таблицы необходимо понять, какие из этих API реально используются в проекте и насколько часто. Я проходилась по каждому пункту, шла в редактор кода, открывала проект и смотрела, сколько раз это свойство из колонки Vue 2 встречается в коде. Например, поиск по .extend дал такой результат:

    Категория

    Vue 2

    Vue 3

    Что делать / чем заменить

    Сколько раз встречается

    Global API

    Vue.extend()

    defineComponent()

    Использовать defineComponent

    5

    В этом примере .extend используется на проекте 5 раз. Значит нужно будет переписать на defineComponent в 5-ти местах. Добавляем ещё одну колонку в нашу таблицу и заносим туда число совпадений. Эта информация будет важна при оценки задачи, чтобы можно было понять сколько будет изменений. 

  3. Создание задач на разработку. По каждой строке из таблицы создаём отдельную dev-task задачу. Мы использовали Jira, но аналогичный процесс можно выстроить в любой платформе для управления разработкой. В задаче описываем, что необходимо сделать и в скольких местах. Также будет полезно приложить ссылки на репозиторий с теми компонентами, в которых надо будет заменить эту логику. В документации по миграции есть описание каждого изменения с примером.

    Пример dev-task задачи в Jiraна миграцию Vue.extend() → defineComponent()

    Пример dev-task задачи в Jira на миграцию Vue.extend() → defineComponent()

    Мы делали так: создавали Epic в Jira по миграции Vue 3, где у нас велась вся разработка. Далее к этому эпику подвязывали все созданные задачи на миграцию из нашей таблицы изменений. Приоритет выше ставили тем, которые incompatible из таблицы с совместимостью. Важно было первоочерёдно закрыть те задачи, которые полностью ломали приложение.

    Пример Epic в Jira миграции Vue 2 → Vue 3 со связанными задачами

    Пример Epic в Jira миграции Vue 2 → Vue 3 со связанными задачами
  4. Задачи на исследования. Если по некоторым пунктам нет точно понимания как переписать компонент на новую логику. Например, мы столкнулись со сложностью удаления внутренних $on/$off/$once. Не буду вдаваться в подробности, однако был legacy-код и нужно было найти более гибкое решение, чем переписать на глобальный Event bus. То мы создавали задачи на инвестигейт (исследование проблемы) и уже по результатам создавали задачку на разработку dev-task.

    Пример investigation-задачи для поиска решения несовместимого legacy API

    Пример investigation-задачи для поиска решения несовместимого legacy API
  5. Оценка задач и сроков. Этот этап может занять много времени, особенно на крупных legacy-проектах. Мы выделяли по 10-20 минут на общих звонках и потихоньку оценивали. В конце это даст понимание, сколько всего времени и сил потребуется. Мы оценивали в Story Points, и конечно, в таком формате точного времени мы не узнаем. Однако, нам было понятно, сколько спринтов и разработчиков на это потребуется, так как на спринт выделялось фиксированное количество Story Points.

Важно понимать: основные сложности обычно возникают не в обновлении версии пакетов, а в legacy-решениях вокруг проекта. В нашем случае больше всего проблем принесли render-functions, event bus, удаление $children, старые scoped slots и большое количество тестов, завязанных на Vue 2.

Следующие этапы

После того, как все задачи созданы, исследования проведены, ресурсы согласованы и выделены — можно будет приступать к миграции:

  • Обновиться до Vue 3 и установить@vue/compat. После перехода на Vue 3 рекомендуется подключить миграционный плагин @vue/compat — специальный режим совместимости, который помогает запускать legacy-код и постепенно устранять несовместимости. Нужно быть готовым к тому, что часть приложения может сломаться. Однако compat позволяет сохранить работоспособность приложения во время миграции и увидеть проблемные места через warnings и runtime-ошибки в консоли.

  • Обновить все плагины до доступных совместимых версий, следуя документации по миграции.

  • Переводить код на Vue 3 API по созданным задачам, постепенно устраняя compat warnings и отключая legacy-фичи через configureCompat или compatConfig в соответствии с разделом Compat Configuration.

  • В конце полностью удалить @vue/compat.

Чем полезен такой подход

Преимущество этого подхода в том, что он превращает миграцию крупного legacy-проекта в набор небольших задач с понятным результатом, что упрощает планирование и снижает риски.

Сложно оценить объём работы не зная точных изменений и количества времени. Предложенное выше планирование даёт оценку того, что нужно будет сделать. Эта оценка возможно не будет на 100% точная. Но разве есть большие комплексные задачи с точной оценкой?

Помним: не надо бежать и всё сразу переписывать на Composition API. Это не будет решать основных проблем в виде уязвимостей, зато добавит больше работ при миграции и тестировании. Берём рабочий и безопасный минимум.

Выводы и мысли

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

Разработчик — это тот, кто умеет понимать бизнес-контекст задачи, доносить важность задач на понятном бизнесу языке, принимать технические решения, оценивать риски и объём работ, планировать этапы реализации, выстраивать архитектуру, проверять гипотезы, анализировать последствия изменений и брать ответственность за общий результат, а не только за строчки кода.

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

По сути, вся миграция на Vue 3 строилась не вокруг «переписать проект», а вокруг последовательного решения маленьких конкретных проблем.

И, наверное, это главный вывод, который я вынесла из этой миграции: большие технические изменения не происходят быстро и одним рывком. Всё становится реальными тогда, когда появляется понятный план, прозрачные процессы и ответственность (ответственный) за каждый этап.

Если после этой статьи миграция перестанет казаться чем-то хаотичным и невозможным — значит, пол дела сделано.

ссылка на оригинал статьи https://habr.com/ru/articles/1042068/