Миграция на Vue 3 начинается не с кода: как донести важность и подготовить проект
- вторник, 2 июня 2026 г. в 00:00:16
«Бизнесу это невыгодно!», «Мы не будем вкладывать в это ресурсы», «Зачем, оно и так отлично работает» — какие аргументы можно привести против всех этих высказываний?
Миграция на 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 и изменения, которые предстоит внести. Когда большой объём работы разбит на понятные небольшие шаги, то миграция перестаёт выглядеть чем-то большим и страшным.
Именно здесь начинается вторая важная часть — подготовка к миграции.
Миграция большого 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. Это важный шаг про внедрение новых процессов.
Чтобы не замораживать основную разработку и не ломать продакшен мы приняли решение процесс распараллелить: основная разработка продолжалась в developers, а миграция на Vue 3 в отдельной ветки с префиксом VUE3, которая была создана от основной developers.
Новые изменения переносились из developers через cherry-pick, после чего при необходимости сразу адаптировались под Vue 3 на основании таблицы миграции, о которой расскажу дальше.
Такой подход позволил продолжать выпускать бизнес-задачи без feature freeze, а миграцию проводить постепенно и контролируемо.
Далее ВАЖНЫЕ шаги подготовки к миграции:
Таблица изменений 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 |
Аудит использования несовместимых API. После составления таблицы необходимо понять, какие из этих API реально используются в проекте и насколько часто. Я проходилась по каждому пункту, шла в редактор кода, открывала проект и смотрела, сколько раз это свойство из колонки Vue 2 встречается в коде. Например, поиск по .extend дал такой результат:
Категория | Vue 2 | Vue 3 | Что делать / чем заменить | Сколько раз встречается |
Global API | Vue.extend() | defineComponent() | Использовать defineComponent | 5 |
В этом примере .extend используется на проекте 5 раз. Значит нужно будет переписать на defineComponent в 5-ти местах. Добавляем ещё одну колонку в нашу таблицу и заносим туда число совпадений. Эта информация будет важна при оценки задачи, чтобы можно было понять сколько будет изменений.
Создание задач на разработку. По каждой строке из таблицы создаём отдельную dev-task задачу. Мы использовали Jira, но аналогичный процесс можно выстроить в любой платформе для управления разработкой. В задаче описываем, что необходимо сделать и в скольких местах. Также будет полезно приложить ссылки на репозиторий с теми компонентами, в которых надо будет заменить эту логику. В документации по миграции есть описание каждого изменения с примером.

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

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

Оценка задач и сроков. Этот этап может занять много времени, особенно на крупных 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 строилась не вокруг «переписать проект», а вокруг последовательного решения маленьких конкретных проблем.
И, наверное, это главный вывод, который я вынесла из этой миграции: большие технические изменения не происходят быстро и одним рывком. Всё становится реальными тогда, когда появляется понятный план, прозрачные процессы и ответственность (ответственный) за каждый этап.
Если после этой статьи миграция перестанет казаться чем-то хаотичным и невозможным — значит, пол дела сделано.