Наставник по миграции с Vue 2 на Vue 3

Строит пошаговый план миграции с Vue 2 на Vue 3 под конкретный проект: оценка блокеров, порядок работ, замена Options API и mixins, совместимость библиотек.

// промпт
Ты — Vue-эксперт, который провёл несколько миграций с Vue 2 на Vue 3 в продакшене. Ты строишь реалистичный план под конкретный проект: сначала блокеры и зависимости, потом механические замены, в конце — осознанный переход на Composition API там, где он окупается. Ты честно говоришь, что мигрировать не стоит. ## Проект - **Версия Vue и размер:** {{versiia_i_razmer_proekta}} (компонентов, строк) - **Ключевые зависимости:** {{osnovnye_biblioteki}} (роутер, стор, UI-кит) - **Использование Vue 2 особенностей:** {{osobennosti}} (mixins, фильтры, $listeners, functional components — что есть) - **Ограничения:** {{ograniceniia}} (сроки, можно ли останавливать фичи) ## Что построить **1. Оценка** - Блокеры: зависимости без Vue 3 версии — и чем заменить каждую - Хребет миграции: роутер (vue-router 4), стор (Vuex → Pinia), сборка (vue-cli → Vite) - Ломающие изменения Vue 3, которые заденут этот проект: v-model на компонентах, фильтры, $children, событийная шина **2. Порядок работ** - Этапы с критерием готовности каждого; что можно катить в прод между этапами - Migration build (@vue/compat) — стоит ли использовать здесь, и когда его снимать - Что переписывать на Composition API сразу (composables вместо mixins), а что оставить на Options API навсегда **3. Риски** - Типичные регрессии после миграции и как их ловить (тесты, canary) - Оценка трудоёмкости по этапам — в диапазонах, с допущениями ## Формат ответа 1. **Вердикт** — мигрировать ли вообще, и каким путём (big bang / поэтапно / compat build). 2. **Таблица блокеров** — зависимость, статус Vue 3, замена. 3. **План по этапам** — шаги, критерии готовности, оценка. 4. **Правила для команды** — 4–5 конвенций, чтобы новый код писался сразу под Vue 3.
Заполните переменные

Пример запуска

Сгенерировано Claude Opus 5.5, 24 сентября 2026 г.

Значения переменных

Версия и размер проекта
Vue 2.7.14, ~340 SFC-компонентов, около 62 000 строк во frontend-репозитории (внутренняя CRM для логистической компании, 4 года в проде)
Основные библиотеки
vue-router 3.6, Vuex 3.6, элементы UI на Element UI (не Element Plus), vue-cli 4 + Webpack, дополнительно vee-validate 3, vue-i18n 8, portal-vue для модалок, moment.js
Особенности
Активно используются mixins (около 40 штук, в т.ч. общий formMixin и tableMixin почти в каждом списке), 2 глобальных фильтра (formatDate, formatMoney) применяются в шаблонах повсеместно, event bus (new Vue()) для общения между несвязанными компонентами (уведомления, обновление счётчиков в шапке), $listeners используется в нескольких обёрточных компонентах над Element UI, functional components — 3 штуки для рендеринга иконок статусов
Ограничения
{"versiia_i_razmer_proekta": "Vue 2.7.14, ~340 SFC-компонентов, около 62 000 строк во frontend-репозитории (внутренняя CRM для логистической компании, 4 года в проде)", "osnovnye_biblioteki": "vue-router 3.6, Vuex 3.6, элементы UI на Element UI (не Element Plus), vue-cli 4 + Webpack, дополнительно vee-validate 3, vue-i18n 8, portal-vue для модалок, moment.js", "osobennosti": "Активно используются mixins (около 40 штук, в т.ч. общий formMixin и tableMixin почти в каждом списке), 2 глобальных фильтра (formatDate, formatMoney) применяются в шаблонах повсеместно, event bus (new Vue()) для общения между несвязанными компонентами (уведомления, обновление счётчиков в шапке), $listeners используется в нескольких обёрточных компонентах над Element UI, functional components — 3 штуки для рендеринга иконок статусов", "ograniceniia": "На миграцию выделено не более 6 недель силами 2 фронтенд-разработчиков параллельно с обычной разработкой фич (не более 30% времени команды на миграцию в неделю), останавливать выпуск новых фич полностью нельзя, регресс-тестирование только ручное силами QA (1 человек, 2 дня в конце каждого этапа), деплой возможен только по вторникам и четвергам"}

Миграция CRM с Vue 2.7 на Vue 3: оценка и план

1. Вердикт

Мигрировать стоит, но не за 6 недель. Путь поэтапный: сначала подготовка на Vue 2.7 с выкладкой в прод, затем короткое переключение в отдельной ветке с временным @vue/compat.

Бюджет. 2 разработчика × 30% × 6 недель ≈ 18 человеко-дней. Полная миграция этого проекта по моей оценке займёт 55–100 человеко-дней (разбивка в разделе 3). Это в 3–5 раз больше выделенного. При темпе 30% полный цикл займёт 5–8 месяцев. Если на 2–3 недели переключения дать команде 70–100% времени, выйдет 3–4 месяца.

Почему мигрировать всё-таки нужно:

  • Vue 2 в EOL с 31.12.2023, то есть уже почти 3 года без патчей.
  • Element UI мёртв.
  • Зависимости заморожены: ни одну ключевую библиотеку нельзя обновить.
  • Растёт стоимость найма и поддержки.

Когда мигрировать не стоит: если CRM планируют заменить в ближайшие 1–2 года. Тогда разумнее остаться на 2.7, при необходимости купить платную расширенную поддержку (HeroDevs NES) и не тратить 3–5 человеко-месяцев. Для внутренней системы за авторизацией риск безопасности умеренный, это не пожар.

Почему не big bang: при 30% загрузки и непрерывной разработке фич долгоживущая ветка «Vue 3» разойдётся с main за 2–3 недели. Её придётся бесконечно ребейзить.

Почему @vue/compat не решает задачу целиком: главный блокер — Element UI. Он завязан на внутренности Vue 2 и на compat надёжно не работает. Значит, UI-кит всё равно меняется одним скачком вместе с рантаймом. Поэтому compat здесь служит диагностическим инструментом на время переключения, а не способом месяцами жить в смешанном режиме.

Стратегия: перенести в main на Vue 2.7 всё, что туда переносится. Тогда ветка переключения становится маленькой, живёт не больше 2–3 недель, а изменения в ней делаются скриптами (codemods), которые можно перезапустить на свежем main.

Что дадут выделенные 6 недель:

  • три релиза подготовительных изменений в прод;
  • прогон переключения на нескольких реальных экранах (spike);
  • откалиброванную оценку и обоснованный запрос бюджета на переключение.

2. Таблица блокеров

Зависимость Статус под Vue 3 Замена Можно ли готовить на 2.7 Комментарий
Element UI ❌ только Vue 2, не поддерживается Element Plus Частично (обёртки, иконки, v-slot) Главный блокер. Меняются иконки (классы el-icon-* → SVG-компоненты из @element-plus/icons-vue), значения size (medium/small/mini → large/default/small), :visible.sync → v-model, type="text" у кнопок, токены value-format у дат (dayjs: YYYY-MM-DD вместо yyyy-MM-dd), SCSS-переменные темы, локали. Codemod от GoGoCode (gogocode-plugin-element) закрывает, по опыту, 50–70% работы, остальное руками.
vee-validate 3 ❌ только Vue 2 vee-validate 4 (другой API: useForm/<Field>) или правила el-form (async-validator) ✅ если выбрать el-form rules Второй по объёму блокер. У el-form rules почти одинаковый API в Element UI и Element Plus, поэтому формы можно переводить на 2.7 постепенно и катить в прод. vee-validate 4 так не получится: он только для Vue 3. Выбор делается в этапе 3 по результатам spike.
vue-router 3.6 ❌ vue-router 4 (или актуальная мажорная, API-совместимая с 4) ❌ только при переключении Меняется mode → createWebHistory(), catch-all * → /:pathMatch(.*)*, onReady → isReady(). У <router-link> убраны tag и event, router.match → resolve. 2–4 дня.
Vuex 3.6 ❌, но есть Vuex 4 с тем же API Vuex 4 при переключении, Pinia 2 постепенно ✅ Pinia 2 работает на 2.7 Не блокер. Переписывать весь стор на Pinia ради миграции не нужно. Важно: Pinia 3 поддержку Vue 2 убрала, поэтому ставим pinia@^2.
vue-cli 4 + Webpack ⚠️ vue-cli 5 умеет Vue 3, но в режиме поддержки Vite (@vitejs/plugin-vue2 сейчас, @vitejs/plugin-vue потом) ✅ полностью Затрагивает process.env.VUE_APP_* → import.meta.env.VITE_*, require() и require.context → import и import.meta.glob, ~-импорты в SCSS, загрузку source maps в Sentry.
vue-i18n 8 ❌ vue-i18n актуальной мажорной (v11 на момент написания; v9/v10 уже EOL) ❌ $t в шаблонах почти не меняется. $tc, v-t и legacy-режим объявлены устаревшими, поэтому при переключении сразу уходим в Composition-режим или ставим задачу на это. Локаль Element Plus подключается отдельно.
portal-vue ⚠️ есть portal-vue 3 Встроенный <Teleport> ❌ У модалок Element Plus уже есть teleported/append-to-body, поэтому часть порталов просто исчезнет.
moment.js ✅ от фреймворка не зависит Оставить, со временем dayjs — Не блокер. Трогать при переключении не нужно, только форматы в пикерах Element Plus.

Ломающие изменения Vue 3, которые заденут именно этот проект

Что Где у вас Готовим на 2.7?
Фильтры удалены formatDate, formatMoney повсюду ✅ Codemod {{ x | formatDate }} → {{ $fmt.date(x) }}. Сейчас через Vue.prototype.$fmt, после переключения через app.config.globalProperties.$fmt, это одна строка.
$on/$off/$once удалены event bus (new Vue()) + паттерн this.$once('hook:beforeDestroy') (проверьте grep'ом, он встречается почти везде) ✅ Шина → mitt за обёрткой с тем же API. Счётчики в шапке лучше перенести в Pinia-стор, шина для них не нужна.
v-model на компонентах: value/input → modelValue/update:modelValue, .sync → v-model:prop Все кастомные инпуты и обёртки ⚠️ Частично: в новых компонентах объявляем model: { prop: 'modelValue', event: 'update:modelValue' }, это работает уже на 2.7.
$listeners слит с $attrs Обёртки над Element UI ❌ При переключении убираем v-on="$listeners". Внимание: при inheritAttrs: false в $attrs теперь попадают class и style, и вёрстка обёрток может «поехать».
.native удалён @click.native на компонентах ❌ На 2.7 он ещё нужен. При переключении удаляем, а в компонентах объявляем emits.
$children удалён Проверить grep'ом ✅ Заменить на ref или provide/inject.
<template functional> удалён 3 иконки статусов ✅ Сделать обычными компонентами, это полчаса.
slot-scope (синтаксис 2.5) Скорее всего в таблицах Element UI ✅ Автофикс eslint-plugin-vue → v-slot / #default="{ row }".
data в mixins сливается поверхностно formMixin: если и миксин, и компонент объявляют form: {...}, в Vue 3 объект компонента затрёт объект миксина целиком ⚠️ Проверить заранее, найти все такие пересечения.
beforeDestroy/destroyed → beforeUnmount/unmounted; Vue.set/$set/$delete удалены; приоритет v-if над v-for; watch на массив требует deep; классы transition v-enter → v-enter-from По всему коду Частично: убрать v-if+v-for на одном элементе, сократить $set. Остальное в ветке переключения, по предупреждениям compat.

3. План по этапах

Общие правила для всех этапов:

  • Этапы выкатываются во вторник. Четверг той же недели остаётся окном для хотфикса или отката. Миграционные изменения в четверг не выкатываем: иначе на выходные уходим без шанса быстро починить.
  • QA (2 дня в конце этапа) работает по чек-листу из 25–35 критичных сценариев (заказы, рейсы, диспетчеризация, отчёты, права). Полная ручная регрессия CRM за 2 дня невозможна, поэтому чек-лист составляется один раз на старте и дальше переиспользуется.
  • Оценки ниже в человеко-днях чистого времени. Один разработчик на 30% даёт примерно 1,5 дня в неделю, двое вместе примерно 3 дня в неделю.

В рамках выделенных 6 недель (≈18 чел.-дней)

Этап 1. Страховочная сетка и чистка на 2.7 (недели 1–2, 6–8 чел.-дней)
Шаг Оценка
Playwright smoke-набор из 12–15 критичных сценариев против стенда. Это главная инвестиция: при одном QA без автотестов каждый следующий этап будет лотереей. 3–4
eslint-plugin-vue: включить vue3-правила с фиксацией текущего уровня (baseline). Существующие нарушения идут как warning, счётчик предупреждений может только уменьшаться (ratchet в CI). 0,5
Фильтры → $fmt.date/$fmt.money (codemod + ручная проверка сложных выражений) 1–1,5
Event bus → mitt-обёртка; счётчики шапки → Pinia-стор; $once('hook:…') → обычные хуки 1–1,5
slot-scope → v-slot (автофикс), $children, functional-иконки, v-if+v-for 0,5–1
  • Критерий готовности: в коде 0 фильтров, 0 new Vue()-шин, 0 slot-scope, 0 $children, 0 functional-шаблонов; smoke-набор зелёный в CI.
  • Выкатка: codemod фильтров отдельным PR, остальное одним релизом. Вторник 3-й недели.
Этап 2. Vite на Vue 2.7 (недели 3–4, 5–7 чел.-дней)
Шаг Оценка
Vite + @vitejs/plugin-vue2, алиасы, env-переменные, require.context → import.meta.glob, SCSS и тема Element UI 3–4
Прод-сборка: чанки, хеши, source maps в Sentry, nginx, CI 1–2
Прогон smoke + сравнение размера бандла 0,5
Параллельно: <AppIcon name> вместо прямых el-icon-*, чтобы при переключении менять одну реализацию, а не сотни мест 1
  • Критерий готовности: прод-сборка идёт через Vite; vue.config.js удалён; время холодного старта dev-сервера измерено (обычно в 5–10 раз быстрее — это аргумент для бизнеса); smoke зелёный.
  • Откат: конфиг vue-cli живёт в ветке ещё 1 релиз.
  • Выкатка: вторник 5-й недели.
Этап 3. Pinia, spike переключения, калибровка (недели 5–6, 5–6 чел.-дней)
Шаг Оценка
pinia@^2 рядом с Vuex. Политика: новые модули только в Pinia. Существующие не переносим. 0,5
Spike в отдельной ветке: Vue 3 + @vue/compat + Element Plus + router 4 на 3 типовых экранах (список с tableMixin, форма с formMixin и vee-validate, модалка через portal). Замерить реальную стоимость одного компонента. Прогнать GoGoCode и оценить долю ручной доводки. 3–4
Решение по валидации: vee-validate 4 или el-form rules. Решение по i18n-режиму. 0,5
Документ с откалиброванной оценкой и запросом бюджета на этапы 4–6 0,5
  • Критерий готовности: 3 экрана работают на Vue 3 в ветке; оценка этапов 4–6 уточнена до ±25%; решения по валидации и i18n зафиксированы.
  • Выкатка: в прод уходит только Pinia-инфраструктура. Spike не мёржится, это исследование.

За пределами 6 недель (нужен отдельный бюджет)

Этап 4. Формы и обёртки на 2.7 (15–25 чел.-дней при 30% ≈ 5–8 недель)
  • Если выбраны el-form rules: формы переводятся пачками по 5–10 и выкатываются каждый вторник. Параллельно tableMixin → useTable() (подробнее ниже).
  • Все обёртки над Element UI сведены к небольшому набору (AppDialog, AppSelect…): при переключении правим обёртку, а не каждое место использования.
  • Критерий готовности: 0 импортов vee-validate; прямые использования el-dialog/el-icon-* только внутри обёрток.
Этап 5. Переключение (25–45 чел.-дней)
  • Прошу на это время исключение: 2–3 недели при 70–100% загрузки. Иначе ветка разойдётся с main.
  • Ветка живёт не дольше 3 недель. Все изменения делаются скриптами, которые перезапускаются на свежем main. Ручные правки ведутся отдельным списком.
  • В одной ветке: Vue 3 + @vue/compat + Element Plus + router 4 + Vuex 4 + i18n + Teleport. Также $listeners, .native, v-model, хуки жизненного цикла.
  • Canary. Для внутренней CRM это идеально: сборка Vue 3 раскатывается на отдельный адрес (next.crm…) с тем же бэкендом. Пилотная группа (один офис или смена диспетчеров, 5–15 человек) работает там 1 неделю. Ошибки отслеживаются в Sentry с тегом сборки. Затем nginx переключает всех по cookie, а старая сборка остаётся доступной для отката ещё 1–2 релиза.
  • Критерий готовности: smoke зелёный; QA-чек-лист пройден; в canary за неделю нет ошибок уровня «не могу работать»; доля ошибок в Sentry не выше, чем у Vue 2.
Этап 6. Снятие compat и доводка (8–15 чел.-дней, 2–4 недели)
  • Переходим на configureCompat({ MODE: 3 }) глобально. Точечные исключения задаём через compatConfig на уровне компонента, по списку предупреждений.
  • Compat снимаем, когда в консоли на smoke-наборе 0 предупреждений, но не позже 2–3 релизов после переключения. Не раньше, потому что compat ловит то, что пропустили codemods. Не позже, потому что compat заметно замедляет рантайм, а команда быстро привыкает его не замечать.

Сводная оценка

Блок Чел.-дни Допущения
Этапы 1–3 (подготовка + spike) 16–21 Укладываемся в 6 недель
Этап 4: формы/обёртки 15–25 60–90 форм, около 15 обёрток
Этап 5: переключение (из них Element Plus 20–35) 25–45 Element используют около 250 из 340 компонентов, GoGoCode закрывает около 50%
Этап 6: compat + стабилизация 8–15 Включая баги после выкатки
Итого ≈ 65–105 Главный разброс даёт Element Plus

Composition API: где переписывать, а где нет

Важно: Vue 2.7 уже поддерживает setup(), <script setup> и composables. Написанное сейчас переедет на Vue 3 без изменений. Mixins в Vue 3 работают, поэтому их переписывание нужно ради ясности кода, а не ради миграции.

Переписываем (окупается):

  1. tableMixin → useTable({ fetch, defaultSort }). Пагинация, сортировка, фильтры, loading, синхронизация с query. Используется почти в каждом списке, значит максимальный выигрыш. Мигрируем постепенно: новый список пишем на composable, старые переводим, когда их всё равно трогаем.
  2. formMixin → useForm(), только вместе со сменой валидации (этап 4). Иначе делаем двойную работу.
  3. Event bus → useNotifications() и стор счётчиков.
  4. Mixins, которые используются в 10+ компонентах или неявно зависят от полей компонента (this.someField без объявления).

Оставляем на Options API навсегда:

  • стабильные страницы, которые не трогаются;
  • простые презентационные компоненты;
  • компоненты на 50–150 строк без переиспользуемой логики.

Options API в Vue 3 — полноценный first-class API, а не устаревший. Переписывать 340 компонентов «для единообразия» — лучший способ сорвать сроки и получить регрессии без пользы.

Типичные регрессии и как их ловить

Регрессия Как ловить
Вёрстка обёрток «поехала» из-за class/style в $attrs Playwright-скриншоты ключевых экранов (toHaveScreenshot) + ручной просмотр обёрток
Двойные обработчики событий (click и из $attrs, и из emit) Обязательные emits в каждом компоненте; smoke-проверка счётчиков и запросов (дублированные POST)
Молча сломанный v-model на кастомных инпутах Smoke: сохранение форм с проверкой данных в ответе API
Даты в пикерах: неверный формат после смены токенов Отдельный пункт QA: создание и фильтрация по датам, часовые пояса
Затёртые данные formMixin из-за поверхностного слияния Grep пересечений до переключения + unit-тест на миксин
Пропавшие реакции на изменения массивов (watch без deep) Предупреждения compat + smoke на экранах со списками
Утечки подписок после удаления $on Sentry + профилирование памяти на длинной сессии диспетчера (они держат CRM открытой весь день)

4. Правила для команды (с первого дня)

  1. Никаких новых mixins. Общая логика оформляется только как composable useXxx() в src/composables/. Новые компоненты пишутся на <script setup> или на Options API без mixins. Всё это работает на 2.7 и без изменений переедет на Vue 3.

  2. Запрещённые API — ошибка линтера в новых и изменённых файлах: фильтры, $on/$off/$once (в том числе hook:-события), new Vue() как шина, $children, slot-scope, <template functional>, this.$set в новом коде (заменять объект целиком). Правила eslint-plugin-vue: no-deprecated-filter, no-deprecated-events-api, no-deprecated-slot-scope-attribute, no-deprecated-functional-template, no-deprecated-dollar-scopedslots-api. .native и $listeners пока оставляем как warning: на 2.7 они ещё легитимно нужны.

  3. Новые компоненты с v-model сразу в формате Vue 3: model: { prop: 'modelValue', event: 'update:modelValue' } и явный emits. Вместо .sync — событие update:xxx. Тогда при переключении их не придётся трогать.

  4. Новое состояние — только в Pinia (pinia@^2), новые модули Vuex не создаются. Для связи несвязанных компонентов используется стор или mitt-обёртка, не шина.

  5. Element UI используется только через наши обёртки (AppIcon, AppDialog, AppSelect…). Новый код не использует классы el-icon-* и :visible.sync напрямую. Даты в новом коде проходят через @/utils/date, прямой импорт moment запрещён: при переключении меняется одна реализация, а не сотни вызовов.


Главное: выделенные 6 недель стоит потратить на то, чтобы переключение стало маленьким, предсказуемым и имело оценку ±25%. Попытка уложить в них всю миграцию закончится незавершённой веткой, которая к концу квартала устареет.

Похожие промпты

Программирование и Разработка

Ревью pull request по diff

Проводит ревью изменений по diff и описанию PR: соответствует ли код заявленной цели, что сломается рядом, чего не хватает в тестах, — и пишет комментарии в формате для GitHub или GitLab.

{} 3 перем.
Программирование и Разработка

Ревью Python-кода: типизация и идиомы

Проводит ревью Python-кода: ошибки, которые проявятся в рантайме, аннотации типов, идиоматичность, обработка исключений и ресурсов — с правками «до/после».

{} 3 перем.
Программирование и Разработка

Ревью SQL-запросов и миграций

Проверяет SQL-запросы и миграции схемы: корректность на граничных данных, индексы под реальные выборки, блокировки при миграции на живой таблице и откат.

{} 3 перем.
Программирование и Разработка

Ревью Next.js-кода: App Router и Server Components

Проводит ревью Next.js-приложения на App Router: границы server/client компонентов, кеширование данных, водопады запросов и лишний клиентский бандл — с правками «до/после».

{} 4 перем.