Vue 2 и Vue 3
Вопрос
Чем Vue 3 отличается от Vue 2 в повседневном коде?
Короткий ответ
Vue 3 переписал реактивность на Proxy, сделал Composition API основным способом делить логику, разрешил несколько корневых узлов и убрал часть глобального API Vue 2. Приложение создаётся через createApp, а не через new Vue с общим конструктором на всю страницу.
| Тема | Vue 2 | Vue 3 |
|---|---|---|
| Реактивность | Object.defineProperty по уже существующим полям. Новое поле — через Vue.set | Proxy. Новые поля отслеживаются. Vue.set нет |
| Описание компонента | Options API. Composition API только плагином | Options API жив. Composition API и <script setup> в ядре |
| Корень шаблона | один корневой узел | фрагменты, несколько корней |
v-model компонента | value и событие input | modelValue и update:modelValue. Несколько v-model |
| Фильтры в шаблоне | {{ price | currency }} | убраны, вместо них функция или computed |
| Глобальные API | Vue.component, Vue.prototype, шина $on | app.component, app.config.globalProperties. Шина убрана |
| Сборки и вход | часто Vue CLI, один глобальный Vue | Vite, createApp на каждый корень |
| Состояние | Vuex | Pinia. Vuex 4 умеет работать с Vue 3, официальная рекомендация — Pinia |
| Роутер | Vue Router 3, mode: 'history' | Vue Router 4, createWebHistory() |
Разбор
v-model на компоненте во Vue 3 можно повесить на разные поля: v-model:title и v-model:open. Модификатор .sync из Vue 2 заменён такой записью. Нативный input по-прежнему удобно связывать через v-model, но контракт своего компонента уже другой: проп modelValue, событие update:modelValue.
$listeners слились в $attrs. inheritAttrs: false по-прежнему отключает автоматическую прокидку атрибутов на корень, только корней теперь может быть несколько, и атрибуты нужно вешать явно.
Ключевые хуки переименованы: beforeDestroy стал beforeUnmount, destroyed стал unmounted. В Composition API те же точки доступны как onBeforeUnmount и onUnmounted.
Фильтры, Vue.filter и методы экземпляра $on / $off / $once удалены. Общую шину заменяют внешним эмиттером или, чаще, стором и пропсами.
На что смотрят на senior
Миграция — не «переписать всё на script setup». Options API во Vue 3 поддерживается. Имеет смысл переписывать компонент, когда логика делится между несколькими экранами или когда типы в Options API мешают. Реактивность на Proxy не поддерживает IE11, это сознательный обрыв.
Смотрят, понимаете ли вы последствия фрагментов: директивы и атрибуты, которые раньше попадали на единственный корень, теперь могут некуда сесть. И понимаете ли контракт emits: объявленные события не утекают в $attrs как нативные слушатели.
Типичные ошибки
- Искать
Vue.setво Vue 3. - Объявлять
v-modelкакvalue+inputна новом компоненте. - Считать, что Composition API обязателен, а Options API выпилен.
- Тащить фильтры и event bus в новый проект.
Задача
Компонент Vue 3 объявляет props: { value: String } и эмитит input. Родитель пишет <Field v-model="name" />. Почему поле не обновляется?
Показать ответ
Во Vue 3 v-model на компоненте ждёт проп modelValue и событие update:modelValue. Пара value / input — контракт Vue 2.
Родитель меняет name только на update:modelValue. Чужое событие input в эту связь не попадает. Либо переименовать контракт, либо на родителе явно написать :model-value и @update:model-value. С 3.4 то же самое короче через defineModel().