← Назад к вопросам

Какие знаешь принципы Flux-архитектуры?

2.3 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Принципы архитектуры Flux

Flux — это архитектурный паттерн для построения клиентских веб-приложений, созданный Facebook для решения проблем согласованности данных и упрощения отладки в сложных интерфейсах. В отличие от традиционных MVC/MVVM, Flux использует однонаправленный поток данных, что делает его поведение более предсказуемым.

Ключевые компоненты Flux

Архитектура состоит из четырех основных частей:

  • Actions (Действия) — это простые объекты, которые описывают, что произошло в приложении (например, USER_CLICKED_BUTTON). Они содержат обязательное поле type и могут нести дополнительные данные (payload). Actions не содержат бизнес-логики.
  • Dispatcher (Диспетчер) — центральный хаб, который регистрирует все callbacks от Store. Каждый Action отправляется через Dispatcher, который затем передает его во все зарегистрированные Store. Это гарантирует строгую очередность и отсутствие каскадных обновлений.
  • Stores (Хранилища) — содержат состояние и бизнес-логику приложения. Они реагируют на Actions, полученные от Dispatcher, обновляют свое состояние и затем испускают событие о том, что состояние изменилось. Store — единственное место, где данные могут меняться.
  • Views (Представления) — это компоненты UI (часто React1-компоненты), которые отображают данные из Stores и отправляют Actions в ответ на пользовательский ввод. При получении события об изменении от Store они перерисовываются.

Основополагающие принципы

  1. Однонаправленный поток данных (Unidirectional Data Flow)
    Это краеугольный камень Flux. Данные всегда движутся по кругу в одном направлении, что делает поток понятным и легким для отслеживания.
```
Action -> Dispatcher -> Store -> View
     ^                                 |
     |_________________________________|
           (Новый Action из View)
```
    Такой подход исключает сложные двусторонние привязки и сводит на нет риск возникновения бесконечных циклов обновления.

  1. Действия (Actions) как описание намерений
    Actions — это не методы, а описания событий. Они говорят *"что случилось"* (например, `TODO_CREATED`), но не *"как изменить состояние"*. Вся логика изменения состояния инкапсулирована внутри Store. Это разделение ответственности упрощает логирование и отладку (можно логировать все Actions и видеть последовательность событий).

  1. Разделение ответственности через Dispatcher
    Dispatcher действует как реестр callbacks. Он гарантирует, что обработка Action не вызовет "водопад" обновлений (когда одно обновление Store приводит к генерации нового Action внутри другого Store в процессе обработки первого). Это достигается через механизм `waitFor()`, который позволяет Store явно объявить зависимости друг от друга.

  1. Единственный источник истины (Single Source of Truth)
    Каждый фрагмент данных в приложении хранится только в одном Store. Все представления, которым нужны эти данные, подписываются на этот Store. Это устраняет противоречия и синхронизацию данных между разными частями приложения.

  1. Изменение состояния только через Actions
    Состояние Store нельзя изменить напрямую извне (например, из View). Единственный способ изменить состояние — отправить Action через Dispatcher. Это делает все мутации централизованными, предсказуемыми и легко отслеживаемыми.

  1. Производные данные вычисляются внутри Store
    Любые вычисления или агрегация данных должны происходить внутри Store до того, как обновленное состояние будет отправлено View. Views остаются "глупыми" и отвечают только за отображение.

Пример кода на JavaScript

Рассмотрим упрощенный пример для добавления новой задачи в todo-лист.

// 1. Константы для типов Actions
const ActionTypes = {
  TODO_CREATE: 'TODO_CREATE'
};

// 2. Action Creator
const TodoActions = {
  create(text) {
    Dispatcher.dispatch({
      type: ActionTypes.TODO_CREATE,
      payload: { text }
    });
  }
};

// 3. Dispatcher (используем синглтон от Facebook)
import { Dispatcher } from 'flux';
const AppDispatcher = new Dispatcher();

// 4. Store
import { EventEmitter } from 'events';

let _todos = []; // Приватное состояние

class TodoStore extends EventEmitter {
  getAll() {
    return _todos;
  }

  emitChange() {
    this.emit('change');
  }

  addChangeListener(callback) {
    this.on('change', callback);
  }
}

const todoStore = new TodoStore();

// Регистрируем Store в Dispatcher
AppDispatcher.register((action) => {
  switch (action.type) {
    case ActionTypes.TODO_CREATE:
      // Единственное место, где меняются данные
      _todos.push({ id: Date.now(), text: action.payload.text });
      todoStore.emitChange(); // Оповещаем View
      break;
  }
});

// 5. View (React-компонент)
class TodoApp extends React.Component {
  constructor() {
    super();
    this.state = { todos: todoStore.getAll() };
  }

  componentDidMount() {
    todoStore.addChangeListener(() => {
      this.setState({ todos: todoStore.getAll() });
    });
  }

  handleClick() {
    // View инициирует Action, а не меняет Store напрямую
    TodoActions.create('Новая задача');
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Добавить</button>
        <ul>{this.state.todos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul>
      </div>
    );
  }
}

Преимущества и развитие идеи

Flux в чистом виде редко используется сегодня из-за своей многословности (boilerplate code) и необходимости ручного управления слушателями. Однако его принципы легли в основу современных и более удобных библиотек управления состоянием. Redux можно считать эволюцией и упрощением Flux: он сохраняет однонаправленный поток, действия как объекты и единственный источник истины (в виде одного корневого Store), но заменяет Dispatcher на чистую функцию-редюсер (reducer) и вводит понятие единого глобального состояния (store).

Таким образом, понимание принципов Flux — это фундамент для эффективной работы с такими инструментами, как Redux, MobX (который предлагает другой, более реактивный подход) и контекстом React в связке с useReducer.