React и Vue это инструменты JavaScript для построения пользовательских интерфейсов из компонентов (React называет себя библиотекой, Vue прогрессивным фреймворком), и у них общие основные идеи: пропсы вниз, события вверх, реактивное состояние, виртуальный DOM. На практике различия такие: React пишет разметку в JSX и ожидает, что состояние заменяют через сеттер, а Vue пишет разметку в шаблонах HTML внутри однофайловых компонентов и позволяет менять реактивные значения напрямую. Вот счётчик на React:
И тот же счётчик как однофайловый компонент Vue (Counter.vue):
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">Clicked {{ count }} times</button>
</template>
useState(0) становится ref(0), onClick становится @click, а {count} становится {{ count }}. Видимое различие это count++: Vue позволяет менять значение на месте, и его система реактивности это замечает. React нужен setCount, потому что он перерендеривает, только когда вы даёте ему новое значение.
JSX или однофайловые компоненты
Компонент React это функция JavaScript, которая возвращает JSX. Условия пишутся через && или тернарный оператор, списки через .map(), а стили берутся из того, что вы выберете (файлы CSS, CSS-модули, утилитарный фреймворк).
Компонент Vue обычно это файл .vue из трёх блоков: <script setup> для логики, <template> для разметки и необязательный <style scoped>, CSS которого применяется только к этому компоненту. Шаблоны используют директивы: v-if, v-for, v-model для двусторонней привязки поля, :prop для привязки значения и @event для прослушивания. Шаблоны оставляют разметку читаемой для любого, кто знает HTML; JSX даёт в разметке весь JavaScript. Vue тоже может использовать JSX, а React не может использовать шаблоны, но большая часть кода в каждом следует своему варианту по умолчанию.
Вот отфильтрованный список, вторая функция для сравнения.
Наберите xyz, чтобы увидеть сообщение о пустом списке, затем удалите. Версия на Vue:
<script setup>
import { ref, computed } from 'vue';
const fruits = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Peach'];
const query = ref('');
const visible = computed(() =>
fruits.filter((f) => f.toLowerCase().includes(query.value.toLowerCase()))
);
</script>
<template>
<input v-model="query" placeholder="Filter fruit" />
<ul>
<li v-for="fruit in visible" :key="fruit">{{ fruit }}</li>
<li v-if="visible.length === 0">No match</li>
</ul>
</template>
v-model заменяет пару value и onChange, а computed заменяет обычную переменную. В React visible пересчитывается при каждом рендере, потому что функция компонента выполняется заново; во Vue computed кэширует его и пересчитывает только при изменении query. Оба используют key, чтобы различать элементы списка.
Модель реактивности
Это самое глубокое различие между ними.
React: неизменяемое состояние и повторные рендеры. Состояние это снимок. Вы вызываете сеттер с новым значением, React снова выполняет функцию компонента, сравнивает новый JSX со старым и обновляет DOM. React сравнивает значения через Object.is, поэтому изменение объекта или массива на месте и передача того же обратно выглядит так, будто ничего не изменилось.
Нажмите «push() in place», и на экране ничего не изменится, потому что массив тот же объект. Затем нажмите «New array»: рендер покажет и pushed, потому что прежняя мутация действительно произошла, просто у React не было причины смотреть. Страница о useState подробно разбирает этот шаблон.
Vue: изменяемые реактивные значения. ref или объект reactive записывает, кто его читает (ref через геттер .value, объект reactive через прокси). Когда вы в него пишете, Vue точно знает, какие вычисляемые значения, наблюдатели и части шаблона от него зависят, и обновляет только их. Во Vue items.value.push('pushed') обновляет экран. Вы пишете меньше церемоний и отказываетесь от гарантии, что значение никогда не изменится у вас за спиной.
Ни одна модель не быстрее настолько, чтобы это имело значение для типичных приложений. React может выполнять больше кода компонентов на одно изменение и полагается на мемоизацию (вручную или через React Compiler), чтобы пропускать работу; Vue отслеживает зависимости во время выполнения и поэтому пропускает эту работу по умолчанию.
Composition API или хуки
Composition API во Vue 3 и хуки React решают одну задачу: собирать связанные состояние и логику вместе и переиспользовать их в разных компонентах. Vue называет переиспользуемые функции composables (useMouse()), React называет их пользовательскими хуками (useMouse()). Правила различаются из-за того, когда выполняется код.
- Когда выполняется.
setupво Vue выполняется один раз при создании компонента. Функция компонента React выполняется при каждом рендере, и хуки внутри неё выполняются вместе с ней. - Правила порядка. React сопоставляет хуки по порядку вызова, поэтому их нельзя вызывать внутри условий или циклов. Во Vue такого правила для
refиcomputedнет, потому что setup выполняется один раз. - Зависимости. Эффекты и мемо в React перечисляют зависимости в массиве, который вы поддерживаете.
watchEffectиcomputedво Vue отслеживают то, что читают, автоматически. - Устаревшие значения. Колбэк React может захватить старое значение состояния из более раннего рендера (классическая ошибка с
setInterval). Колбэк Vue читаетcount.valueв момент вызова и всегда видит текущее значение.
Модель React делает каждый рендер чистой функцией от пропсов и состояния, что легко тестировать и обдумывать. Модель Vue убирает целый класс ошибок с массивами зависимостей. Разработчики, знающие одно, обычно быстро осваивают другое.
Экосистема
| React | Vue | |
|---|---|---|
| Разметка | JSX | Шаблоны HTML в файлах .vue (JSX по желанию) |
| Обновления состояния | Заменить через сеттер, затем повторный рендер | Изменить реактивное значение, зависимые обновятся |
| Переиспользование логики | Пользовательские хуки | Composables |
| Производные значения | Вычисляются во время рендера, useMemo | computed (кэшируется) |
| Двусторонняя привязка поля | value плюс onChange | v-model |
| Изолированный CSS | Не встроен (CSS-модули, библиотеки) | <style scoped> встроен |
| Маршрутизация | React Router, TanStack Router, фреймворки | Vue Router (официальный) |
| Глобальное состояние | Контекст, Zustand, Redux Toolkit | Pinia (официальная) |
| Фреймворк полного стека | Next.js, React Router в режиме фреймворка | Nuxt |
| Мобильные приложения | React Native | Официального варианта нет (Capacitor, NativeScript-Vue) |
| Инструмент сборки | Vite или фреймворк | Vite |
| Кто поддерживает | Meta и сообщество | Независимая основная команда и сообщество |
Закономерность в этой таблице: основная команда Vue поддерживает роутер, хранилище и devtools вместе, поэтому у проекта на Vue для каждого есть очевидный вариант по умолчанию. React оставляет это сообществу, что даёт больше вариантов и больше решений. Vite, обычный инструмент сборки для новых проектов в обоих, начал создатель Vue.
Порог входа
Vue часто даёт более мягкий старт. Шаблоны читаются как уже знакомый вам HTML, v-model убирает шаблонный код контролируемых полей, а реактивное значение обновляет экран, когда вы его меняете. React сразу требует JSX и привычку к неизменяемым обновлениям, и страница о JSX это первое, что нужно большинству.
После основ разрыв сокращается. Vue добавляет свои понятия (ref или объекты reactive, .value в скриптах, но не в шаблонах, наблюдатели), а React добавляет эффекты, мемоизацию и правила хуков. Вакансии в вашем регионе это более полезный решающий довод, чем синтаксис.
Когда что выбрать
Выбирайте React, когда:
- Вам нужен широкий выбор библиотек и компонентов сообщества или вы планируете делать нативные мобильные приложения на React Native.
- Вам нравится держать логику и разметку в одной функции JavaScript и нужен фреймворк вроде Next.js.
- Ваша команда ценит явные неизменяемые обновления, благодаря которым каждый рендер легко отследить.
Выбирайте Vue, когда:
- Вам нужны официальные согласованные инструменты для маршрутизации и состояния и меньше решений.
- В вашей команде есть дизайнеры или бэкенд-разработчики, которым удобны шаблоны HTML.
- Вы добавляете интерактивность на существующие страницы с серверным рендерингом: Vue можно подключить на страницу тегом script и развивать дальше.
Оба зрелые и используются в продакшене компаниями любого размера, а понятия, изученные в одном (компоненты, пропсы, производное состояние, списки с ключами), напрямую переносятся в другой.
Часто задаваемые вопросы
Vue проще, чем React?
Многим новичкам Vue сначала кажется проще, потому что шаблоны похожи на обычный HTML, а изменение реактивного значения (count.value++) обновляет экран без сеттера. React требует изучить JSX и правило, что состояние нужно заменять, а не менять. После первых недель сложность примерно одинаковая.
Что лучше: React или Vue?
В целом ни один не выигрывает. React оставляет маршрутизацию и состояние библиотекам сообщества и имеет React Native для мобильных приложений. У Vue есть официальные роутер, хранилище и devtools, которые поддерживаются вместе, и шаблоны, которые многим проще читать. Выбирайте то, что использует ваша команда или рынок вакансий.
Чем Composition API во Vue отличается от хуков React?
Оба группируют логику с состоянием в переиспользуемые функции. setup во Vue выполняется один раз на компонент и отслеживает зависимости автоматически, поэтому нет массивов зависимостей и устаревших замыканий. Функция компонента React выполняется при каждом рендере, поэтому хуки должны вызываться каждый раз в одном порядке, а эффекты перечисляют свои зависимости.
Vue использует JSX?
Vue поддерживает JSX через плагин, но большая часть кода на Vue использует шаблоны HTML в однофайловых компонентах (файлах .vue). React почти везде использует JSX.
Какой аналог Next.js во Vue?
Nuxt. Как Next.js для React, он добавляет поверх Vue файловую маршрутизацию, серверный рендеринг, статическую генерацию и серверные маршруты.