Slim.js — легковесная библиотека для создания веб-компонентов на JavaScript с реактивной системой данных. Одним из ключевых паттернов для построения масштабируемых компонентов является provider/consumer, который позволяет организовать передачу данных и зависимостей между компонентами без необходимости пробрасывать их через каждый уровень дерева компонентов.
Provider — это компонент, который предоставляет данные или сервисы. Он создаёт контекст, доступный для всех его потомков, которые захотят использовать эти данные.
Consumer — компонент, который получает данные от ближайшего родителя-provider. Consumer подписывается на изменения данных и автоматически обновляется при их изменении.
Ключевые преимущества паттерна:
Для определения provider используется свойство provide в
компоненте. Оно может принимать объект с любыми значениями, которые
должны быть доступны потомкам.
Пример:
import { Slim } from 'slim-js';
class ThemeProvider extends Slim {
static get observedAttributes() {
return ['theme'];
}
constructor() {
super();
this.theme = 'light';
}
provide() {
return {
theme: this.theme
};
}
onAttributeChange(attr, oldVal, newVal) {
if (attr === 'theme') {
this.theme = newVal;
this.update(); // обновление компонента и передача нового значения потомкам
}
}
render() {
return `
<slot></slot>
`;
}
}
customElements.define('theme-provider', ThemeProvider);
Ключевые моменты:
provide() возвращает объект, доступный для всех
потомков.update()), чтобы потребители
получили актуальные значения.Consumer использует свойство consume для подписки на
данные provider. Slim.js автоматически ищет ближайший ancestor с
соответствующим provide и связывает данные.
Пример:
class ThemeConsumer extends Slim {
consume() {
return ['theme'];
}
render() {
return `
<div>Текущая тема: ${this.theme}</div>
`;
}
}
customElements.define('theme-consumer', ThemeConsumer);
Особенности работы:
consume() возвращает массив имён свойств, которые
компонент хочет получать от provider.Slim.js поддерживает иерархию provider/consumer. В этом случае каждый consumer получает данные от ближайшего provider в дереве компонентов.
Пример вложенности:
<theme-provider theme="dark">
<div>
<theme-consumer></theme-consumer>
<theme-provider theme="light">
<theme-consumer></theme-consumer>
</theme-provider>
</div>
</theme-provider>
theme: dark.theme: light, так как
внутренний provider перекрывает внешний.Provider может обновлять данные в любой момент, и все подписанные consumer автоматически получат новые значения.
setTimeout(() => {
document.querySelector('theme-provider').theme = 'dark';
}, 2000);
После обновления theme все consumer перерисуются с новым
значением.
Хотя provider/consumer решает большинство задач передачи данных, иногда нужны пользовательские события. Slim.js позволяет комбинировать оба подхода:
class CounterProvider extends Slim {
constructor() {
super();
this.count = 0;
}
provide() {
return { count: this.count };
}
increment() {
this.count++;
this.update();
this.dispatchEvent(new CustomEvent('count-changed', { detail: this.count }));
}
render() {
return `<slot></slot>`;
}
}
Consumer может подписываться на событие count-changed
для дополнительных действий, помимо получения данных через
consume().
Паттерн provider/consumer в Slim.js обеспечивает мощный и реактивный способ организации данных между компонентами, позволяя строить гибкие, поддерживаемые и масштабируемые приложения без сложного проброса props через множество уровней.