В одном из основных принципов разработки приложений с использованием фреймворка Svelte лежит концепция реактивности. Однако, несмотря на свою простоту, стандартный подход к реактивности через компоненты может быть недостаточен для работы с более сложными состояниями, которые необходимо разделять между множеством компонентов. Для решения этой задачи в Svelte предусмотрены stores — механизм для управления глобальным состоянием, доступным во всей структуре приложения.
Store в Svelte представляет собой объект, который хранит данные и предоставляет механизмы для подписки и обновления этих данных. Подписчики получают уведомления, когда данные в store изменяются, что позволяет автоматически обновлять компоненты, использующие эти данные. Это даёт возможность эффективно управлять состоянием приложения, не прибегая к сторонним библиотекам для работы с глобальным состоянием.
Svelte предоставляет несколько типов store, каждый из которых имеет свою сферу применения: writable, readable и derived.
Наиболее часто используемый тип store — writable. Этот store позволяет не только читать данные, но и изменять их.
Для создания writable store используется функция
writable():
import { writable } from 'svelte/store';
const count = writable(0);
В примере выше создаётся store с начальным значением 0. Для изменения
значения этого store используется метод .set():
count.set(5); // устанавливаем новое значение
Кроме того, можно использовать метод .update(), который
позволяет изменять состояние на основе его текущего значения:
count.update(n => n + 1); // увеличиваем значение на 1
Чтобы подписаться на изменения значения store, можно использовать
конструкцию $store внутри компонента:
<script>
import { count } from './store.js';
</script>
<p>Значение: {$count}</p>
<button on:click={() => count.update(n => n + 1)}>Увеличить</button>
В этом примере значение count автоматически обновляется
при каждом изменении.
Readable store используется, когда состояние данных доступно только для чтения, но не для записи. Это может быть полезно, когда нужно предоставить данные для использования в разных частях приложения, но не разрешать изменять их.
Для создания readable store используется функция
readable():
import { readable } from 'svelte/store';
const time = readable(new Date(), set => {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return () => clearInterval(interval);
});
В этом примере time обновляется каждую секунду с текущим
временем. Вызываемая функция set используется для установки
нового значения, а возврат из функции, переданной в
readable(), устанавливает очистку интервала при уничтожении
компонента.
Подписка на time в компоненте будет аналогична подписке
на любой другой store:
<script>
import { time } from './store.js';
</script>
<p>Текущее время: {$time}</p>
Derived store используется для создания новых store на основе других store. Это позволяет создавать состояние, которое зависит от других значений, сохраняя реактивность и консистентность данных.
Для создания derived store используется функция
derived():
import { derived } from 'svelte/store';
import { count } from './store.js';
const doubledCount = derived(count, $count => $count * 2);
Здесь doubledCount будет автоматически обновляться при
изменении count, и всегда будет в два раза больше значения
count. Подписка на derived store осуществляется так же, как
и на другие store:
<script>
import { doubledCount } from './store.js';
</script>
<p>Удвоенное значение: {$doubledCount}</p>
Store также предоставляет несколько вспомогательных методов для удобства работы с состоянием:
subscribe() — позволяет подписаться
на store вручную. Этот метод возвращает функцию отписки:
const unsubscribe = count.subscribe(value => {
console.log(value);
});
// Отписка от store
unsubscribe();set() — для установки нового
значения в writable store.
update() — для обновления значения
на основе текущего состояния.
Для использования глобальных stores в компонентах, Svelte
автоматически отслеживает изменения и обновляет DOM. Чтобы извлечь
данные из store, достаточно использовать синтаксис $store,
где store — это название вашего хранимого объекта.
Пример использования writable store:
<script>
import { count } from './store.js';
</script>
<p>Счёт: {$count}</p>
<button on:click={() => count.update(n => n + 1)}>Увеличить</button>
В этом компоненте значение count будет автоматически
обновляться каждый раз, когда пользователь нажимает кнопку, а DOM
перерисовывается с новым значением.
Для использования других типов store, например, readable или derived, синтаксис остаётся аналогичным. Главное отличие состоит в том, что вы не можете изменять данные в readable store или store, полученном через derived.
Когда приложение становится более сложным и включает в себя несколько различных состояний, использование stores позволяет организовать данные централизованно, без необходимости передавать их через props или событий в компонентах. В таком случае можно создать один или несколько глобальных stores для управления состоянием и использовать их в компонентах по всему приложению.
Пример:
// store.js
import { writable } from 'svelte/store';
export const user = writable({
name: 'Иван',
email: 'ivan@example.com'
});
Затем в любом компоненте можно подписаться на данные этого store:
<script>
import { user } from './store.js';
</script>
<p>Имя: {$user.name}</p>
<p>Электронная почта: {$user.email}</p>
Используя такие глобальные stores, можно избежать необходимости передачи данных через множество компонентов и упростить управление состоянием приложения.
Использование stores в Svelte значительно упрощает управление состоянием в сложных приложениях. Это не только снижает необходимость в дополнительных библиотеках для глобального состояния, но и предоставляет мощные и удобные механизмы для работы с реактивными данными, улучшая производительность и читаемость кода.