✌️🆚⚛️ 6 фич, которые реализованы во Vue лучше, чем в React
Рассказываем, почему Vue не только не уступает самому популярному инструменту для разработки фронтенда, но и во многом его превосходит.
Автор оригинала этой статьи долгое время был убежденным приверженцем 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? Поделитесь своими впечатлениями и трудностями, с которыми вы столкнулись в процессе.