Что такое stores в Svelte? Объясните writable, readable и derived stores.
writable — store с методами set и update, readable — store только для чтения (внешние источники данных), derived — вычисляется из других stores. Все три реализуют контракт { subscribe } и совместимы с авто-подпиской через $.
Stores в Svelte
Store — любой объект с методом subscribe(callback), который вызывает callback немедленно с текущим значением и при каждом его изменении, а также возвращает функцию отписки. Svelte предоставляет три готовых примитива в пакете svelte/store.
writable — записываемый store
import { writable } from 'svelte/store';
const count = writable(0); // начальное значение
// set — установить новое значение
count.set(5);
// update — изменить на основе текущего
count.update(n => n + 1);
// subscribe — подписаться на изменения
const unsubscribe = count.subscribe(value => {
console.log('count:', value); // вызовется сразу с 0, потом при каждом изменении
});
// Отписаться
unsubscribe();
readable — store только для чтения
Используется для данных из внешних источников: таймеры, WebSocket, геолокация. Второй аргумент — функция start, вызываемая при первом подписчике; возвращает stop для очистки.
import { readable } from 'svelte/store';
// Store текущего времени, обновляется каждую секунду
const time = readable(new Date(), function start(set) {
const interval = setInterval(() => set(new Date()), 1000);
// Вызывается при последнем отписчике
return function stop() {
clearInterval(interval);
};
});
// Использование в компоненте:
// <p>{$time.toLocaleTimeString()}</p>
derived — производный store
Вычисляется из одного или нескольких stores. Обновляется автоматически при изменении исходников.
import { writable, derived } from 'svelte/store';
const price = writable(100);
const quantity = writable(3);
// Из одного store
const doubled = derived(price, $price => $price * 2);
// Из нескольких stores
const total = derived(
[price, quantity],
([$price, $quantity]) => $price * $quantity
);
// Асинхронный derived — третий аргумент: начальное значение
const asyncData = derived(
url,
($url, set) => {
fetch($url)
.then(r => r.json())
.then(set);
return () => {}; // cleanup
},
null // начальное значение до первого resolve
);
Кастомный store
Можно создать store с инкапсулированной логикой, экспортируя только нужные методы:
import { writable } from 'svelte/store';
function createCart() {
const { subscribe, update, set } = writable([]);
return {
subscribe, // обязателен для $ авто-подписки
addItem: (item) => update(items => [...items, item]),
removeItem: (id) => update(items => items.filter(i => i.id !== id)),
clear: () => set([])
};
}
export const cart = createCart();
// В компоненте:
// import { cart } from './stores.js';
// cart.addItem({ id: 1, name: 'Товар' });
// {$cart.length} товаров
Подводные камни
- readable не запускает start до первой подписки — если никто не подписан, данные не обновляются. Это экономит ресурсы, но может удивить при первом use.
- derived пересчитывается синхронно для каждого обновления зависимости — тяжёлые вычисления в
derivedмогут тормозить UI; используйте debounce внутри. - Подписка в обычном JS возвращает unsubscribe — забытый вызов отписки — утечка памяти. Только в
.svelteфайлах$управляет этим автоматически. - Начальное значение derived по умолчанию
undefined— если используется в шаблоне без fallback, может рендерить пустоту до первого вычисления. - Циклические зависимости между derived-stores приводят к бесконечному циклу обновлений.
- update() не атомарен при множественных вызовах подряд — если нужно изменить несколько stores одновременно, используйте
svelte/storebatch(или рефакторите в один store). - В Svelte 5 stores остаются для обратной совместимости, но для нового кода рекомендуются руны
$stateи$derived.
Common mistakes
- Путать Svelte stores с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «Svelte stores».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.