Middleware и плагины для состояния

Slim.js — это лёгкая и высокопроизводительная библиотека для построения веб-компонентов с реактивным состоянием. Одной из ключевых возможностей Slim.js является поддержка middleware и плагинов для работы с состоянием, что позволяет гибко управлять потоками данных и расширять функциональность компонентов.


Middleware: концепция и применение

Middleware в Slim.js представляет собой промежуточный слой между изменением состояния и его отражением в DOM. Он позволяет перехватывать, модифицировать или фильтровать данные перед их применением к компоненту.

Основные задачи middleware:

  • Логирование изменений состояния.
  • Валидация данных.
  • Синхронизация с внешними источниками (API, localStorage, серверные события).
  • Преобразование данных перед отображением.

Пример использования middleware:

import { Slim, middleware } from 'slim-js';

const logMiddleware = (state, next) => {
  console.log('Состояние до изменения:', state);
  next(state); // передаёт управление следующему middleware или обновлению компонента
  console.log('Состояние после изменения:', state);
};

class Counter extends Slim {
  static get use() {
    return [middleware(logMiddleware)];
  }

  constructor() {
    super();
    this.count = 0;
  }

  increment() {
    this.count++;
  }

  render() {
    return `<button oncl ick="${() => this.increment()}">Count: ${this.count}</button>`;
  }
}

customElements.define('my-counter', Counter);

В этом примере middleware logMiddleware перехватывает все изменения состояния компонента, что позволяет контролировать процесс обновления и отлаживать приложения.


Плагины для состояния

Плагины в Slim.js — это расширения, которые интегрируются с системой состояния и добавляют новые возможности без модификации базового компонента. Плагины могут предоставлять:

  • Поддержку асинхронного состояния.
  • Декларативную синхронизацию с внешними хранилищами.
  • Реактивные вычисляемые свойства.
  • Инструменты для кэширования и оптимизации ререндеринга.
Пример плагина: синхронизация с localStorage
import { Slim, plugin } from 'slim-js';

const localStoragePlugin = plugin((component, key) => {
  const savedValue = localStorage.getItem(key);
  if (savedValue !== null) {
    component[key] = JSON.parse(savedValue);
  }

  component.onChange(key, (value) => {
    localStorage.setItem(key, JSON.stringify(value));
  });
});

class TodoList extends Slim {
  static get use() {
    return [localStoragePlugin];
  }

  constructor() {
    super();
    this.todos = [];
  }

  addTodo(todo) {
    this.todos = [...this.todos, todo];
  }

  render() {
    return `
      <ul>
        ${this.todos.map(todo => `<li>${todo}</li>`).join('')}
      </ul>
      <input type="text" onkeypr ess="${(e) => {
        if (e.key === 'Enter') this.addTodo(e.target.value);
      }}">
    `;
  }
}

customElements.define('todo-list', TodoList);

Этот плагин автоматически сохраняет и восстанавливает состояние компонента из localStorage, обеспечивая простую персистентность данных.


Реактивные вычисляемые свойства

Slim.js поддерживает вычисляемые свойства, которые обновляются автоматически при изменении зависимостей. Middleware и плагины могут использовать эту возможность для оптимизации и кэширования данных.

class ProductList extends Slim {
  constructor() {
    super();
    this.products = [];
    this.filter = '';
  }

  get filteredProducts() {
    return this.products.filter(p => p.name.includes(this.filter));
  }

  render() {
    return `
      <input type="text" onin put="${(e) => this.filter = e.target.value}" placeholder="Фильтр">
      <ul>
        ${this.filteredProducts.map(p => `<li>${p.name}</li>`).join('')}
      </ul>
    `;
  }
}

customElements.define('product-list', ProductList);

Использование вычисляемых свойств совместно с middleware позволяет, например, логировать фильтрацию, синхронизировать её с сервером или кэшировать результаты для ускорения ререндеринга.


Композиция middleware и плагинов

Slim.js позволяет комбинировать несколько middleware и плагинов одновременно. Порядок подключения имеет значение: middleware вызываются по цепочке в порядке подключения, а плагины — в порядке регистрации.

class App extends Slim {
  static get use() {
    return [middleware(logMiddleware), localStoragePlugin];
  }

  constructor() {
    super();
    this.data = '';
  }

  render() {
    return `<div>${this.data}</div>`;
  }
}

customElements.define('my-app', App);

Такой подход обеспечивает гибкость управления состоянием и позволяет строить сложные реактивные компоненты с минимальной связностью.


Особенности реализации

  • Middleware получает текущее состояние и функцию next для передачи управления следующему слою.
  • Плагины могут подписываться на изменения отдельных свойств через onChange и добавлять реактивную логику.
  • Совместное использование middleware и плагинов позволяет создавать архитектуру, похожую на Flux или Vuex, но без лишнего веса и сложности.
  • Важная практика — избегать синхронных блокирующих операций внутри middleware, чтобы не замедлять ререндеринг компонентов.

Практическая рекомендация

  • Для логирования и отладки использовать middleware.
  • Для синхронизации с API или внешними хранилищами — плагины.
  • Вычисляемые свойства и реактивные подписки позволяют минимизировать лишние обновления DOM.
  • Последовательность подключения middleware и плагинов должна соответствовать логике обработки данных: сначала валидация, потом трансформация, затем синхронизация и логирование.