Автор оригинала этой статьи долгое время был убежденным приверженцем React и наотрез отказывался работать с другими библиотеками и фреймворками. Но однажды он все же решился попробовать Vue, и удивился тому, насколько удобнее и рациональнее там сделаны многие вещи.
1. Установка пакета и создание приложения
Первый шаг на
пути к созданию React-приложения
— простая команда npm create-react-app
. Ничего интересного в процессе выполнения этой команды вы не увидите. А
вот установка Vue с помощью npm create vue@latest
— совсем другое дело: это интерактивный процесс,
во время которого вы можете выбрать, что вам нужно, а что — нет:

Откройте localhost после установки Vue — и вы увидите удобную стартовую страницу со ссылками на все нужные ресурсы:

2. Интуитивная система реактивности
Обработка реактивных данных и автоматическое обновление пользовательского интерфейса во Vue происходит более интуитивно и с меньшим количеством избыточного кода по сравнению с управлением состоянием в React. При использовании Vue разработчикам не нужно беспокоиться о механизмах обновления, они просто объявляют реактивные данные, и фреймворк автоматически отслеживает и обновляет то, что нужно:
<template>
<p>{{ count }}</p>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const count = ref(0);
onMounted(() => {
setInterval(() => {
count.value++;
}, 1000);
});
onUnmounted(() => clearInterval(interval));
</script>
React требует больше шаблонного кода для достижения похожей реактивности, особенно когда речь идет об эффектах и обновлениях состояния, которые зависят от предыдущих значений состояния. Кроме того, React-разработчикам приходится постоянно обращаться к документации, чтобы вспомнить тонкие различия между хуками useEffect, useMemo и useCallback:
function Counter() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
const interval = setInterval(() => {
setCount(currentCount => currentCount + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <p>{count}</p>;
}
3. Инкапсуляция кода компонента в одном файле
Vue позволяет инкапсулировать весь код компонента (HTML, JavaScript и CSS сразу) в одном файле с расширением .vue. У этого подхода есть и сторонники, и противники. Сторонники считают, что инкапсуляция обеспечивает лучшую организацию и читаемость кода, особенно для небольших компонентов: можно просматривать и модифицировать все части компонента в одном месте, не переключаясь между разными файлами:
<script setup lang="ts">
import { defineProps } from 'vue'
defineProps({
message: String
})
</script>
<template>
<div class="message-box">
<p>{{ message }}</p>
</div>
</template>
<style scoped>
.message-box {
padding: 20px;
border: 1px solid #eee;
}
</style>
В React же для достижения подобной инкапсуляции обычно разделяют код на разные файлы (JavaScript, CSS) или используют библиотеки CSS-in-JS, что добавляет лишние сложности. Например, в приведенном ниже примере на React:
- Функциональный компонент MessageBox определен в JavaScript-файле.
- HTML-разметка и логика компонента объединены в одной функции.
- CSS-стили нужно определить в отдельном CSS-файле или с помощью CSS-in-JS.
function MessageBox({ message }) {
return (
<div className="message-box">
<p>{message}</p>
</div>
);
}
// CSS-стили находятся в отдельном файле или подключаются с помощью CSS-in-JS
4. Условный рендеринг
Vue использует систему директив (v-if, v-else-if, v-else и v-for) для условного рендеринга. Эти директивы:
- Добавляются непосредственно в HTML-шаблон компонента, в результате код становится более читаемым и понятным.
- Позволяют сохранять понятную структуру кода даже при использовании сложных условий.
В приведенном ниже примере директивы используются для рендеринга элемента:
- v-if, если условие истинно.
- v-else-if, если первое условие ложно, но второе истинно.
- v-else, если все предыдущие условия ложны.
<script setup lang="ts">
import { ref } from 'vue';
const isSubscribed = ref(false);
const isSubscriptionExpiring = ref(true);
// Перечисленные выше свойства обновляются динамически
</script>
<template>
<div>
<p v-if="!isSubscribed">Please subscribe to access premium features.</p>
<p v-else-if="isSubscriptionExpiring">Your subscription is expiring soon. Please renew to continue enjoying premium features.</p>
<p v-else>Welcome back, valued premium user!</p>
</div>
</template>
Логика эквивалентного кода на React менее очевидна:
function SubscriptionStatus() {
const isSubscribed = false;
const isSubscriptionExpiring = true;
// Перечисленные выше свойства обновляются динамически
return (
<div>
{isSubscribed ? (
isSubscriptionExpiring ? (
<p>Your subscription is expiring soon. Please renew to continue enjoying premium features.</p>
) : (
<p>Welcome back, valued premium user!</p>
)
) : (
<p>Please subscribe to access premium features.</p>
)}
</div>
);
}
Разумеется, в React можно вынести логику условного рендеринга в отдельные функции, что улучшит читаемость кода. Но нельзя отрицать, что логика примера на Vue с использованием директив выглядит гораздо понятнее.
5. Вычисляемые свойства
Vue предоставляет удобный способ объявления вычисляемых свойств с помощью computed. Это позволяет инкапсулировать сложную логику, зависящую от реактивных данных, в отдельной функции. Вычисляемые свойства автоматически кэшируются и перевычисляются только при изменении их зависимостей, что хорошо сказывается на производительности.
<script setup lang="ts">
import { computed, ref } from 'vue'
const price = ref(10)
const quantity = ref(3)
const totalPrice = computed(() => price.value * quantity.value)
</script>
<template>
<p>Total price: {{ totalPrice }}</p>
</template>
В React для достижения аналогичной функциональности используется хук useMemo. Он позволяет кэшировать результат вычисления на основе зависимостей, указанных во втором аргументе. По сравнению с простым и элегантным подходом Vue, синтаксис useMemo кажется более громоздким:
function TotalPrice() {
const [price, setPrice] = React.useState(10);
const [quantity, setQuantity] = React.useState(3);
const totalPrice = React.useMemo(() => {
return price * quantity;
}, [price, quantity]);
return <p>Total price: {totalPrice}</p>;
}
6. Управление состоянием
Для управления глобальным состоянием во Vue чаще всего используется библиотека Pinia. Благодаря бесшовной интеграции с фреймворком, Pinia позволяет работать с состоянием приложения с использованием привычного синтаксиса Vue, без лишних оберток или сложных настроек:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
actions: {
increment() {
this.count++
},
},
})
// components/MyCounter.vue
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<button @click="counter.increment">Increment</button>
<div>Current Count: {{ counter.count }}</div>
</template>
Для управления состоянием в React используется популярная библиотека Redux. Хотя Redux предоставляет мощный инструментарий для управления состоянием, код получается более сложным:
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1
}
},
})
export const { increment } = counterSlice.actions
export default counterSlice.reducer
// Counter.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
export function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return (
<button onClick={() => dispatch(increment())}>
Increment
</button>
<span>{count}</span>
)
}
Подведем итоги
Хотя React остается самым популярным инструментом для создания фронтенда, знакомство с Vue может существенно расширить горизонты любого веб-разработчика: в ряде ключевых областей этот фреймворк предлагает более рациональные и производительные решения. Продуманный дизайн API и акцент на удобстве разработки позволили Vue достичь баланса между мощностью и простотой использования: фреймворк объединяет лучшие идеи и практики из других библиотек, оставаясь при этом лаконичным, гибким и легким в освоении. Это делает Vue отличным выбором как для небольших проектов, так и для масштабных корпоративных приложений.
Есть ли у вас опыт перехода с React на Vue? Поделитесь своими впечатлениями и трудностями, с которыми вы столкнулись в процессе.
Комментарии
Поднимите руку React-разработчики, которым реально "приходится постоянно обращаться к документации, чтобы вспомнить тонкие различия между хуками useEffect, useMemo и useCallback"
22 июля 2024, npm create-react-app wtf?