УДК 004 Антонов С.А., Вуколов А.А., Кононыхина К.А.
Антонов С.А.
2 курс магистратуры «Информационные системы и телекоммуникации» Московский государственный технический университет им. Н.Э. Баумана
(г. Москва, Россия)
Вуколов А.А.
2 курс магистратуры «Информационные системы и телекоммуникации» Московский государственный технический университет им. Н.Э. Баумана
(г. Москва, Россия)
Кононыхина К.А.
2 курс магистратуры «Информационные системы и телекоммуникации» Московский государственный технический университет им. Н.Э. Баумана
(г. Москва, Россия)
ОБЗОР СОВРЕМЕННЫХ БИБЛИОТЕК ДЛЯ РАЗРАБОТКИ ИНТЕРФЕЙСА ВЕБ ПРИЛОЖЕНИЯ
Аннотация: в статье проводится детальный анализ современных библиотек и фреймворков, которые играют ключевую роль в создании интерфейсов. В условиях быстро меняющегося технологического ландшафта такие инструменты, как React, Redux, Vue и Angular, становятся ключевыми компонентами для разработки качественных и интерактивных веб-приложений. В статье рассматривается архитектура и принципы работы каждого из этих инструментов, а также их значение при построении динамических пользовательских интерфейсов. Оцениваются достоинства и ограничения данных технологий при использовании их на практике, учитывая аспекты производительности, масштабируемости и интеграции с другими технологиями. Также проводится анализ влияния сообщества разработчиков на данные инструменты, доступности поддержки и документации - все это имеет весомое значение при выборе оптимального решения для создания приложений в условиях постоянно меняющегося мира веб-разработки. Цель статьи заключается в предоставлении разработчикам и специалистам по техническим вопросам
возможности сориентироваться среди множества доступных решений для осознанного выбора наилучшей библиотеки или фреймворка, соответствующих поставленным целям при создании web-приложений.
Ключевые слова: веб-разработка, фреймворк, библиотека, интерфейс, обзор, React, Angular, Vue, Redux, Redux Toolkit, Effector, MobX, менеджеры состояний.
Современная веб-разработка переживает стремительное развитие, диктуя новые стандарты и подходы к созданию пользовательских интерфейсов. В условиях растущих требований к производительности, масштабируемости и интерактивности веб-приложений, выбор подходящих инструментов для разработки становится одной из ключевых задач для разработчиков. В последние годы особую популярность приобрели библиотеки и фреймворки языка программирования JavaScript, такие как React, Redux, Vue и Angular. Каждый из этих инструментов обладает уникальными возможностями, которые могут существенно упростить процесс создания современных веб-приложений, обеспечивая при этом высокий уровень гибкости и контроля над кодом.
Тем не менее, разнообразие доступных решений часто ставит разработчиков перед сложным выбором: какой инструмент окажется наиболее подходящим для конкретного проекта? Чтобы ответить на этот вопрос, мы детально рассмотрим сильные и слабые стороны каждой из упомянутых библиотек, их архитектурные особенности, а также то, как они вписываются в общую экосистему веб-разработки. Эта статья призвана помочь разработчикам лучше ориентироваться в многообразии современных технологий, за счет предоставления комплексного обзора инструментов, а также рекомендаций по их применению в различных сценариях.
Введение.
Angular, React и Vue — это три ведущих инструмента, которые определяют современный подход к веб-разработке и служат основой для создания высококачественных и интерактивных веб-приложений. Все три технологии придерживаются компонентного подхода, который стал неотъемлемой частью разработки сложных пользовательских интерфейсов. Этот подход позволяет разбивать интерфейс на небольшие, независимые компоненты, каждый из которых выполняет определенную функцию и может быть повторно использован в различных частях приложения.
Компонентный подход существенно улучшает процесс разработки, делая код более модульным, управляемым и легким в поддержке. В каждом из рассматриваемых в данной статье инструментов разработки компоненты обладают своей собственной логикой, состоянием и представлением, что позволяет разработчикам создавать приложения, которые легко масштабируются и адаптируются к изменениям. Более того, такой подход способствует созданию более структурированного и организованного кода, что особенно важно при работе над крупными проектами.
Представленные фреймворки и библиотеки предлагают разработчикам широкий набор возможностей для решения самых разнообразных задач. Несмотря на различия в архитектуре и философии, каждый из инструментов обеспечивает высокую производительность и гибкость, что позволяет создавать современные веб-приложения с богатым пользовательским интерфейсом и сложной бизнес-логикой. Также рассматриваемые инструменты поддерживают интеграцию с различными экосистемами, что делает их универсальными и подходящими для различных типов проектов, будь то небольшие одностраничные приложения или масштабные корпоративные системы.
React.
React — это библиотека, разработанная FB, которая помогает создавать современные веб-интерфейсы и приложения. Основная идея React заключается в разбиении пользовательского интерфейса на небольшие, независимые кусочки, называемые компонентами. Эти компоненты можно легко создавать, комбинировать и повторно использовать, что делает работу над проектом проще и эффективнее. В качестве компонентов можно представить кнопку, поле для ввода данных, блок с текстом и так далее. Каждый из этих элементов можно сделать отдельным компонентом, который работает самостоятельно и не зависит от других [2, с. 14-15].
React предлагает два основных способа создания компонентов: классовые и функциональные. Классовые компоненты раньше были основным способом работы в React. Они используют специальный синтаксис языка программирования JavaScript, который называется «класс». Пример классового компонента представлен в листинге 1. В таких компонентах можно хранить состояние — информацию, которая может меняться, например, количество нажатий на кнопку, а также методы изменения и обработки данного компонента. Например, в классовом компоненте можно описать методы так называемого "жизненного цикла", то есть его поведение при монтировании (т.е. создании экземпляра компонента и его вставке в страницу), при изменении каких-либо параметров, или при размонтировании (т.е. удалении компонента из страницы веб-приложения). Однако, такие компоненты могут быть сложнее в написании и понимании.
Листинг 1. Пример классового компонента.
import React, { Component } from 'react';
class ExampleComponent extends Component {
// Инициализация локального состояния компонента constructor(props) { super(props); this.state = { data: null, isLoading: true,
};
}
// Вызывается сразу после монтирования компонента (отрисовка в DOM)
componentDidMount() {
// Симуляция загрузки данных setTimeout(() => {
this.setState({ data: 'Данные загружены', isLoading: false }); }, 2 0 0 0);
}
// Вызывается при обновлении компонента (изменении состояния или параметров, переданных в компонент ("пропсов")) componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: компонент обновлён'); console.log('Предыдущее локальное состояние:', prevState); console.log('Текущее состояние:', this.state);
}
// Вызывается перед размонтированием компонента (удаление из DOM) componentWillUnmount() {
console.log('componentWillUnmount: компонент размонтируется');
}
// Метод рендера, возвращает JSX разметку для отображения render() {
const { data, isLoading } = this.state;
return ( <div>
<М>Пример классового компонента</^Ь1> {isLoading ? <р>Загрузка...</р> : <p>{data}</p>} </div>
);
}
}
export default ExampleComponent;
Функциональные компоненты — это более простой и современный способ написания компонентов (ниже, в листинге 2 приведен пример). Они представляют собой обычные функции JavaScript, которые так же, как и классовые компоненты, возвращают JSX - это HTML-подобная разметка прямо в JavaScript-коде. Хотя JSX выглядит как HTML, под капотом он компилируется в вызовы JavaScript-функций, например, React.createElement. JSX упрощает создание и управление компонентами, комбинируя разметку и логику в одном месте. С помощью специальных инструментов, называемых хуками (например, useState и useEffect), функциональные компоненты тоже могут управлять состоянием и жизненным циклом. По сравнению с классовыми компонентами, этот подход стал более популярным, потому что он проще и является более интуитивным.
Листинг 2. Пример функционального компонента.
import React, { useState, useEffect } from 'react';
const ExampleComponent = () => {
// Хук useState для управления состоянием
const [data, setData] = useState(null);
const [isLoading, setlsLoading] = useState(true);
// Хук useEffect для выполнения побочных эффектов, например, загрузки данных useEffect(() => {
console.log('Компонент смонтирован');
// Симуляция загрузки данных setTimeout(() => {
setData('Данные загружены'); setlsLoading(false); }, 2 0 0 0);
// Очистка при размонтировании компонента return () => {
console.log('Компонент размонтирован');
};
}, []); // Пустой массив означает, что эффект выполнится только при первом рендере
return ( <div>
СЬ1>Пример функционального компонента</Ь.1 >
{isLoading ? <р>Загрузка...</р> : <p>{data}</p>} </div>
) ;
} ;
export default ExampleComponent;
Одна из особенностей React — это использование виртуального DOM. DOM (Document Object Model) — объектная модель документа, это структура, в которой браузер хранит и отображает содержимое веб-страницы. Обычно, когда что-то меняется на странице, браузеру нужно обновить структуру, что может занять время и замедлить работу сайта. React решает эту проблему, создавая в памяти легкую копию данной структуры, называемую виртуальным DOM. Когда что-то меняется, React сначала обновляет виртуальный DOM, сравнивает его с предыдущей версией и находит, какие именно части нужно изменить. Затем React делает минимальные изменения в реальном DOM, что делает обновления страницы более быстрыми и эффективными.
Стоит отметить, что React — это библиотека, а не полноценный фреймворк, поэтому для создания полноценного приложения часто приходится дополнительно интегрировать другие инструменты и библиотеки. Например, для управления состоянием в сложных приложениях можно использовать Redux (о нем будет написано далее), для маршрутизации — React Router. Это добавляет сложности в настройку проекта, особенно для новичков. Помимо этого React постоянно развивается и обновляется, и экосистема вокруг него тоже. Новые версии React, а также связанные с ним библиотеки и инструменты, выходят регулярно, что требует от разработчиков постоянного обучения и адаптации. Из-за этого может возникнуть сложность в поддержке и обновлении уже существующего кода.
Angular.
Angular — это популярный фронтенд-фреймворк для создания клиентских приложений на языке TypeScript. Angular разработан компанией Google и используется для создания масштабируемых веб-приложений с богатым пользовательским интерфейсом [3, с. 23]. Его ключевая особенность — модульность и компонентный подход, что делает разработку сложных приложений более структурированной и поддерживаемой.
В основе Angular лежит компонентный подход (в листингах 3 и 4 приведены соответственно логика и шаблон компонента). Приложение состоит из отдельных компонентов, каждый из которых представляет собой самостоятельный блок пользовательского интерфейса (UI). Компоненты в Angular включают HTML-шаблон для отображения данных, TypeScript для обработки логики и стили для оформления. Это позволяет организовать код более структурированно и легко поддерживать приложение.
Angular делит приложения на модули. Каждый модуль может содержать несколько компонентов, сервисов и других функциональных частей. Модули помогают организовать приложение и управлять зависимостями между различными частями. Главный модуль — это AppModule, с которого начинается работа приложения.
Помимо этого, Angular использует декораторы — это специальные аннотации, которые необходимы для работы с классами. Например, с помощью декоратора @Component создаются компоненты, @NgModule — модули, а @Injectable позволяет объявить сервисы, которые можно инжектировать в компоненты и другие сервисы.
Angular имеет встроенную систему маршрутизации, которая позволяет навигировать между различными компонентами и страницами приложения без перезагрузки страницы. Это позволяет легко создавать одностраничные приложения (SPA). Angular поддерживает сложные схемы маршрутизации, такие как защита маршрутов (guard), редиректы, и ленивую загрузку модулей, что помогает улучшить производительность приложения.
Также стоит отметить директивы Angular — это специальные инструкции, которые Angular использует для работы с DOM (Document Object Model), среди которых есть структурные, которые изменяют структуру DOM, добавляя или удаляя элементы (например, *ngIf или *ngFor), атрибутные -изменяют поведение или стиль существующих элементов (например, [ngClass]), и компонентные - директивы с собственными шаблонами.
Листинг 3. Логика компонента.
import { Component } from '@angular/core'; @Component({
selector: 'app-hello', // Имя селектора для использования компонента в HTML
templateUrl: './hello.component.html', // Путь к шаблону
компонента
styleUrls: ['./hello.component.css'] // Путь к файлу стилей
компонента })
export class HelloComponent {
message: string = 'Привет, Angular!';
changeMessage() {
this.message = 'Вы нажали на кнопку!';
}
}
Листинг 4. Шаблон компонента.
<h1>{{ message }}</h1>
<button (click)="changeMessage()">Нажми меня</button>
После того, как компонент создан, его необходимо добавить в главный компонент приложения (обычно app.component.html), что показано в листинге 5:
Листинг 5. Вставка созданного компонента.
<app-hello></app-hello>
Vue.
Vue — это прогрессивный фреймворк для создания пользовательских интерфейсов. Vue стремится минимизировать сложность разработки, предлагая понятный и декларативный синтаксис.
Vue использует концепцию виртуального DOM, однако он старается оптимизировать работу с ним и минимизировать перерисовки, автоматически определяя, какие компоненты нужно обновить [1, c. 24-27]. В React это приходится делать вручную, оптимизируя компоненты при помощи shouldComponentUpdate или React.memo.
Основная структура Vue-компонента состоит из трёх частей: шаблона (template), логики (script) и стилей (style). В отличие от React, где JSX используется для разметки, Vue использует HTML-подобный синтаксис с добавлением директив, например v-if или v-for, которые используются для условного рендера и циклического отображения данных, соответственно. Такие Vue-компоненты называются Однофайловыми Компонентами (Single File Component, SFC). В листинге 6 приведен пример Однофайлового Компонента.
Листинг 6. Однофайловый Компонент.
<script> export default { data () { return { count: 0
}
},
methods: {
increment() { this.count++
}
},
}
</script> <template>
<button @click="increment">Счётчик: {{ count }}</button> </template>
<style scoped> button {
font-weight: bold;
}
</style>
Внутри тега template находится разметка, которая будет отображаться в DOM. Для динамического отображения данных используется интерполяция с помощью двойных фигурных скобок, которая выводит значение переменной counter. Vue, так же как и React, основан на реактивности, поэтому при изменении данных, шаблон автоматически обновится.
Логика компонента описана в теге script. Здесь определяется объект с основными параметрами компонента. Функция data() возвращает объект с состоянием (в данном случае, это counter), а в блоке methods прописываются методы, которые будут реагировать на события, такие как клик по кнопке.
Внутри тега style задаются стили компонента. Использование атрибута scoped означает, что стили применяются только к текущему компоненту и не повлияют на другие компоненты. Если атрибут scoped не указан, то стили применяются глобально.
Одним из преимуществ Vue является то, что он, в отличии от React, предоставляет множество встроенных решений. Например, Vue Router и Vuex — это инструменты, которые официально поддерживаются и интегрируются без необходимости настройки от сторонних библиотек.
Vuex используется для управления глобальным состоянием. Он позволяет хранить данные в едином хранилище, к которому могут обращаться все компоненты приложения. Это помогает синхронизировать данные между разными компонентами и упрощает управление состоянием в больших приложениях.
Vue Router интегрируется с Vue для создания маршрутов и навигации между страницами или компонентами без перезагрузки страницы. Внутри Vue, маршруты определяются как объекты, которые связывают URL с конкретными компонентами. Vue Router также поддерживает динамические параметры,
вложенные маршруты и защиту маршрутов, что делает его мощным инструментом для создания одностраничных приложений (SPA).
Менеджеры состояний.
Стейт-менеджер (или менеджер состояния) — это инструмент, позволяющий управлять состоянием web-приложения. Состояние - это данные, которые используются в приложении и могут изменяться в процессе его работы. К таким данным относится информация о пользователе, данные с сервера, глобальные параметры UI (например тема или язык интерфейса) и так далее.
В небольших приложениях состоянием можно управлять с помощью локальных переменных и свойств компонентов. Однако по мере усложнения приложения и увеличения объема данных управлять состоянием без дополнительных инструментов становится трудно. Для улучшения читаемости кода и упрощения контроля за потоками данных следует использовать стейт-менеджеры.
Основная задача стейт-менеджера — централизовать и упорядочить управление состоянием, обеспечивая согласованность данных и упрощая их передачу между компонентами. Компоненты могут подписываться на изменение состояния и автоматически обновляться при его изменении.
В сложных приложениях компоненты находятся на разных уровнях вложенности, в таких приложениях часто требуется передавать данные от родительского компонента дочерним. В случае если стейт-менеджер не используется и состояние приложения хранится в локальных переменных, данные необходимо передавать через свойства промежуточных компонентов, на рисунке 1 приведена иллюстрация такого способа организации потока данных. При таком подходе увеличивается связность компонентов и растет общая сложность системы. Также ухудшается общая производительность приложения, так как при передаче данных между компонентами на разных уровнях вложенности все промежуточные компоненты должны будут обновиться, несмотря на то, что новые данные ими напрямую не используются, а просто
передаются дочерним компонентам. Эта проблема получила название "Props Drilling".
Рисунок 1. Управление потоком данных без использования стейт-менеджера.
Использование стейт-менеджеров позволяет передавать данные между компонентами напрямую, не используя промежуточные компоненты. Таким образом уменьшается связность системы, облегчается переиспользование компонентов и увеличивается общая производительность web-приложения. На рисунке 2 приведена иллюстрация организации потока данных при использовании стейт-менеджера.
Данные
Корневой компонент
Рисунок 2. Управление потоком данных при помощи стейт-менеджера.
Рассмотрим наиболее популярные стейт-менеджеры, которые используются для разработки современных web-приложений.
Redux.
Redux — это библиотека управления состоянием приложений JavaScript, наиболее часто используемая с библиотекой React, хотя может работать с любым UI-фреймворком [2, c. 190]. Она была создана, чтобы упростить процесс управления состоянием в сложных приложениях, особенно при работе с взаимодействиями компонентов и побочными эффектами. Основная идея Redux заключается в том, чтобы создать предсказуемую и централизованную архитектуру управления состоянием, где состояние хранится в одном месте — в глобальном хранилище (store) (пример работы с глобальным хранилищем приведен в листинге 8). Вся информация (состояние) приложения хранится в
одном объекте (глобальном хранилище). Это помогает избежать ситуации, когда разные компоненты или модули по-разному интерпретируют состояние. С централизованным хранилищем состояние приложения становится легко предсказуемым, и его поведение можно легко отслеживать с помощью инструментов, таких как Redux DevTools. Единственный способ изменить состояние — это отправить (dispatch) действие (action) (пример действия приведен в листинге 6), описывающее, как состояние должно измениться. Это создает четкое разделение между тем, что происходит (логика), и тем, как это влияет на данные (изменение состояния). Для описания того, как состояние изменяется в ответ на действие, используются редьюсеры (reducer) (пример редьюсера приведен в листинге 7), которые являются чистыми функциями. Чистые функции гарантируют, что один и тот же ввод всегда будет возвращать один и тот же результат, что делает поведение приложения предсказуемым. Далее в листинге 9 будет приведен пример базовой архитектуры стейт-менеджера Redux с подключением компонента счетчика к стору.
Листинг 6. Действия (actions).
export const increment = () => ({
type: 'INCREMENT', }) ;
export const decrement = () => ({
type: 'DECREMENT', }) ;
Листинг 7. Редьюсер (reducer).
const initialState = { count: 0,
} ;
const counterReducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT':
return { ...state, count: state.count + 1 }; case 'DECREMENT':
return { ...state, count: state.count - 1 }; default:
return state;
}
};
export default counterReducer;
Листинг 8. Глобальное хранилище (store).
import { createStore } from 'redux'; import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
Листинг 9. React компонент с подключением к Redux.
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './actions';
const Counter = () => {
const count = useSelector((state) => state.count); const dispatch = useDispatch();
return ( <div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>+</button> <button onClick={() => dispatch(decrement())}>-</button> </div>
);
};
export default Counter;
В приведенном выше компоненте используются два хука: useSelector для доступа к хранилищу и useDispatch для отправки действий. Кроме того, подключаемый компонент должен быть обернут в так называемый "провайдер" - компонент высшего порядка, или HOC (High Order Component), параметром которого является созданное ранее хранилище store, что показано в листинге 10:
Листинг 10. Подключение компонента к глобальному хранилищу.
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; import Counter from './Counter';
ReactDOM.render(
<Provider store={store}>
<Counter /> </Provider>,
document.getElementByld('root' )
) ;
Redux-Saga — это middleware для Redux, которое позволяет управлять побочными эффектами в приложениях. Он предоставляет мощные инструменты для обработки сложных асинхронных потоков, например, запросов к API (пример приведен в листинге 11), таймеров, и т.д. Redux-Saga основывается на генераторах JavaScript (функции, которые могут "приостанавливаться" и возобновляться), что позволяет создавать пошаговые инструкции для выполнения побочных эффектов, а также позволяет писать код в более синхронном стиле, несмотря на асинхронные операции.
Листинг 11. Пример саги для асинхронного запроса к API.
import { call, put, takeEvery } from 'redux-saga/effects'; import { fetchDataSuccess, fetchDataFailure } from './actions';
// Симуляция запроса к API const fetchDataFromApi = () =>
fetch('https://test.com/data').then(response => response.json());
// Сага для обработки запроса данных function* fetchDataSaga() { try {
const data = yield call(fetchDataFromApi); // Асинхронный вызов
API
yield put(fetchDataSuccess(data)); // Данные успешно
получены
} catch (error) {
yield put(fetchDataFailure(error.message)); // Обработка ошибки
}
}
// Watcher-сага: следит за действиями function* watchFetchData() {
yield takeEvery('FE TCH_DATA_RE QUEST
}
export default watchFetchData;
В данном примере watchFetchData наблюдает за действиями типа FETCH_DATA_REQUEST и на каждое такое действие вызывает сагу fetchDataSaga, в которой происходит запрос данных через API. Ключевое слово yield - это ключевое слово в JavaScript, которое используется в функциях-генераторах (объявляемых с помощью function*). Оно позволяет функции приостанавливать выполнение и возвращать промежуточное значение, сохраняя
'FETCH_DATA_RE QUEST' ', fetchDataSaga);
свое текущее состояние. После этого выполнение можно возобновить с того же места. В контексте Redux-Saga, yield используется для приостановки выполнения саги до получения результата асинхронной операции (например, call, put и других эффектов). Это позволяет писать асинхронный код в стиле синхронного, что делает его более читаемым.
Таким образом, использование Redux-Saga особенно полезно в крупных и сложных проектах, где требуются сложные манипуляции с состоянием и асинхронными процессами.
Redux Toolkit.
Redux Toolkit (RTK) — это официальная библиотека для упрощения работы с Redux, которая предоставляет набор инструментов для более удобной, эффективной и безопасной разработки. RTK значительно уменьшает объем кода, который обычно требуется для настройки Redux, устраняя необходимость вручную настраивать такие аспекты, как middlewares и devtools. Он предоставляет функцию configureStore() (пример кода приведен в листинге 12), которая автоматически включает стандартные middlewares и поддерживает интеграцию с Redux DevTools, что упрощает начальную настройку. Помимо этого Redux Toolkit использует библиотеку Immer, которая позволяет изменять состояние, используя мутации, но при этом сохраняя его неизменным (immutability). Это делает работу с состоянием намного проще, поскольку не нужно вручную клонировать объекты и следить за сохранением неизменяемости, как это требуется в чистом Redux. В стандартном Redux нужно много шаблонного (boilerplate) кода: создание экшенов, редьюсеров, типов и их связывание. RTK через функции createSlice() и createAction() упрощает этот процесс. С createSlice() создаются и экшены, и редьюсеры в одном месте, а не в отдельных файлах, что уменьшает объем кода и упрощает его поддержку. Пример создания редьюсера и экшенов приведен в листинге 13.
Листинг 12. Конфигурация хранилища с помощью RTK.
import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice';
// Создаем стор
const store = configureStore({ reducer: {
counter: counterReducer, // добавляем редьюсер к хранилищу
},
}) ;
export default store;
Листинг 13. Создание редьюсера и экшенов с помощью RTK.
import { createSlice } from '@reduxjs/toolkit';
// Определяем начальное состояние const initialState = { value: 0,
};
// Создаем слайс с экшенами и редьюсером const counterSlice = createSlice({ name: 'counter', initialState, reducers: {
increment: (state) => {
state.value += 1; // Используем мутации через Immer
},
decrement: (state) => { state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload; // Добавляем к значению
переданное значение },
},
}) ;
// Экспортируем экшены, чтобы использовать их в компонентах export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// Экспортируем редьюсер, чтобы добавить его в стор export default counterSlice.reducer;
Effector.
Effector — это библиотека для управления состоянием в web-приложениях. Effector позволяет описывать логику управления данными при помощи декларативных конструкций, что существенно упрощает разработку и поддержку кода [4].
При декларативном подходе разработчик описывает что должно быть сделано, а не как это должно быть сделано. В декларативных конструкциях акцент делается на описании конечного результата, а не на пошаговых инструкциях для его достижения. Противоположность декларативного подхода - императивный подход.
При императивном подходе разработчик описывает как именно должна выполняться задача. Разработчик выдает конкретные инструкции, которые компьютер должен пошагово выполнить в определенном порядке для достижения желаемого результата. Существенным недостатком такого подхода является невысокий уровень абстракции, разработчик должен заботиться о мелких деталях реализации, вместо решения самой задачи.
Управление потоками данных в Effector осуществляется при помощи Юнитов. Юнит (Unit) - это базовый тип данных, который используется для описания и управления состоянием приложения. Существует три базовых Юнита: Стор (Store), Событие (Event) и Эффект (Effect). Рассмотрим каждый из них подробнее.
Стор представляет собой контейнер для состояния. Это неизменяемый объект, который содержит значение и может изменяться только через заранее определенные механизмы (например, через события или эффекты). Стор обновляется только тогда, когда получает значение, которое не равно текущему и не равно undefined. В приложении могут совместно существовать множество состояний.
Событие — это функция, на вызов которой можно подписаться, чтобы реагировать на происходящие в приложении действия. Активация события запускает цепочку реактивных вычислений. События позволяют обновлять
значения сторов, инициировать эффекты или вызывать другие события, обеспечивая гибкое и управляемое взаимодействие между различными частями приложения. Пример использования событий представлен в листинге 14.
Эффект — это контейнер для сайд-эффектов. Сайд-эффект (побочный эффект) - это чаще всего асинхронная операция, которая может быть вызвана внутри приложения для взаимодействия с "внешним миром", например, запрос на сервер или работа с таймерами. Эффекты управляют побочными действиями в приложении и могут изменять сторы в зависимости от результата выполнения. Пример использования эффектов представлен в листинге 15.
Листинг 14. Пример использования событий и сторов в Effector.
import { createStore, createEvent } from 'effector';
// Создаем событие для инкремента счетчика const increment = createEvent();
// Создаем стор для счетчика, с начальным значением 0 const $counter = createStore(O);
// Стор $counter подписывается на событие инкремента $counter.on(increment, (state) => state + 1);
// Функция watch позволяет подписываться на изменения стора $counter.watch(state => {
console.log('Current counter value:', state); }) ;
// Вызываем событие инкремента increment(); // Current counter value: 1 increment(); // Current counter value: 2
Листинг 15. Пример использования эффектов в Effector.
import { createEffect, createStore } from 'effector';
// Создаем эффект для асинхронного запроса к API const fetchDataFx = createEffect(async () => {
const response = await fetch('https://test.com/data'); if (!response.ok) {
throw new Error('Ошибка загрузки данных);
}
return response.j son(); }) ;
// Создаем стор для хранения данных const $data = createStore(null);
// Обновляем стор при успешной загрузке данных и сбрасываем в
случае возникновении ошибки
$data
.on(fetchDataFx.doneData, (_, data) => data) .on(fetchDataFx.fail, () => null);
MobX.
MobX — это библиотека для управления состоянием в JavaScript-приложениях, которая предлагает простой и реактивный подход к работе с данными. Работая на основе реактивного программирования, MobX позволяет автоматически отслеживать изменения в данных и синхронизировать их с пользовательским интерфейсом.
Принцип работы MobX заключается в том, что состояние приложения (данные) представляется в виде обычных объектов, массивов и примитивов JavaScript. Эти данные можно напрямую изменять, а библиотека автоматически отслеживает такие изменения и обновляет те части приложения, которые зависят
от изменившихся данных. Такой подход устраняет необходимость в сложных структурах и делает код более прозрачным.
MobX основан на трёх ключевых концепциях: observables, реакции и вычисления. Observables — это данные, которые изменяются со временем, такие как переменные или объекты. MobX автоматически отслеживает любые изменения в них и инициирует обновления в компонентах, которые зависят от этих данных. Реакции — это фрагменты кода, которые выполняются в ответ на изменения состояния, например, обновление интерфейса при изменении значений. Вычисления (или вычисляемые значения) представляют собой производные от состояния данные, которые пересчитываются только при изменении исходных observables, что позволяет оптимизировать производительность.
Пример работы с MobX представлен в листинге 16. Предположим, у нас есть класс TodoStore, который управляет списком задач. В этом классе хранится массив задач ftodos'), который является observable. Когда мы добавляем новую задачу в этот список через метод 'addTodo\ MobX автоматически отслеживает изменения. Если у нас есть вычисляемое свойство Л unfimshedTodos,, которое возвращает количество незавершённых задач, то оно будет пересчитываться только при изменении списка задач, что оптимизирует работу приложения.
Листинг 16. Пример работы с MobX.
import { makeAutoObservable } from "mobx";
class TodoStore {
todos = []; // Массив задач (observable)
constructor() {
// Автоматическое отслеживание изменений в свойствах объекта makeAutoObservable(this);
}
// Метод для добавления новой задачи в список addTodo(todo) {
this.todos.push(todo);
}
// Вычисляемое свойство: возвращает список незавершенных задач get unfinishedTodos() {
return this.todos.filter(todo => !todo.completed);
}
}
// Создание экземпляра хранилища const todoStore = new TodoStore();
// Добавление новой задачи todoStore.addTodo({
title: "Изучить MobX", completed: false }) ;
// Логирование количества незавершенных задач
console.log(todoStore.unfinishedTodos); // Выведет задачу "Изучить MobX"
Преимущество MobX заключается в том, что разработчику не нужно вручную обновлять интерфейс или контролировать, какие компоненты должны измениться при изменении состояния. MobX делает это автоматически. Такой подход значительно упрощает разработку и уменьшает количество рутинного кода. В отличие от других библиотек, таких как Redux, MobX не требует следования строгим архитектурным шаблонам, что делает его более гибким в использовании. У пользователя есть возможность изменять состояние напрямую, без необходимости создавать actions или reducers, как в случае с Redux.
Использование MobX особенно оправдано в небольших и средних приложениях, где важна простота и гибкость. Он также подходит для ситуаций, когда нужно обрабатывать сложные зависимости между данными и интерфейсом. В то же время, в больших приложениях с большим числом разработчиков более строгие подходы, как у Redux, могут оказаться предпочтительными благодаря своей предсказуемости и строгим правилам.
Вывод.
Современные библиотеки и фреймворки для разработки веб-интерфейсов, такие как React, Angular и Vue, представляют собой мощные инструменты для создания интерактивных и масштабируемых приложений. Все они используют компонентный подход, что упрощает разработку и поддержку сложных систем, предоставляя возможности для повторного использования кода и улучшения структуры проекта. React отличается своей гибкостью, но требует интеграции сторонних инструментов для создания полноценных приложений. Его использование виртуального DOM делает обновления страницы быстрыми и эффективными, а функциональные компоненты с хуками упрощают управление состоянием. В то же время Angular предлагает более комплексное решение из коробки, включая встроенные средства маршрутизации, управление состоянием и модульную архитектуру. Это делает его подходящим для крупных корпоративных приложений, хотя может усложнять освоение для начинающих
разработчиков. А Vue сочетает простоту и мощь, обеспечивая встроенные решения для управления состоянием и маршрутизацией, что делает его удобным для разработки как небольших, так и крупных приложений. Его гибкость позволяет разработчикам сосредоточиться на бизнес-логике, избегая излишней сложности.
Менеджеры состояний, такие как Redux, MobX и Effector, помогают организовать и централизовать управление состоянием в приложениях, что особенно важно для масштабируемости и предсказуемости поведения.
В зависимости от архитектуры и задач проекта, каждый инструмент имеет свои преимущества, позволяя разработчикам выбирать наиболее подходящий для их нужд подход.
СПИСОК ЛИТЕРАТУРЫ:
1. Эрик Хэнчетт, Бенджамин Листуон Vue.js в действии // СПб.: Питер: ООО издательство "Питер", 2024 — 304 с;
2. А. Бэнкс React и Redux: функциональная веб-разработка // СПб.: Питер: ООО издательство "Питер", 2022 — 336 с;
3. А. Фримен Angular для профессионалов // СПб.: Питер: ООО издательство "Питер", 2023 — 800 с;
4. Effector, документация [Электронный ресурс]. — URL: https://effector.dev/en/api/effector/ (дата обращения: 18.09.2024).
Antonov S.A., Vukolov A.A., Kononikhina K.A.
Antonov S.A.
Bauman Moscow State Technical University (Moscow, Russia)
Vukolov A.A.
Bauman Moscow State Technical University (Moscow, Russia)
Kononikhina K.A.
Bauman Moscow State Technical University (Moscow, Russia)
OVERVIEW OF MODERN LIBRARIES FOR DEVELOPING WEB APPLICATION INTERFACE
Abstract: the article provides a detailed analysis of modern libraries and frameworks that play a key role in creating interfaces. In a rapidly changing technological landscape, tools such as React, Redux, Vue and Angular are becoming key components for developing high-quality and interactive web applications. The article discusses the architecture and principles of operation of each of these tools, as well as their importance in building dynamic user interfaces. The advantages and limitations of these technologies are evaluated when using them in practice, taking into account aspects of performance, scalability and integration with other technologies. The analysis of the influence of the developer community on these tools, the availability of support and documentation is also carried out - all this is of great importance when choosing the optimal solution for creating applications in the ever-changing world of web development. The purpose of the article is to provide developers and technical specialists with the opportunity to navigate among the many available solutions for an informed choice of the best library or framework that meets their goals when creating web applications.
Keywords: web development, framework, library, interface, overview, React, Angular, Vue, Redux, Redux Toolkit, Effector, MobX, state managers.