Общее состояние в файлах .svelte.js
Пока список задач живёт в +page.svelte, другие компоненты получают его только через свойства. Когда одни и те же данные нужны в нескольких местах — счётчик в шапке, список на странице, фильтр в боковой панели, — состояние выносят в отдельный модуль. Руны работают в файлах с расширением .svelte.js.
Модуль с состоянием
Создайте файл src/lib/todos.svelte.js:
// src/lib/todos.svelte.js
export const todoStore = $state({
items: [
{ id: 1, text: 'Купить хлеб', done: false },
{ id: 2, text: 'Позвонить Олегу', done: false }
]
});
export function addTodo(text) {
todoStore.items.push({ id: Date.now(), text, done: false });
}
export function toggleTodo(id) {
const todo = todoStore.items.find((t) => t.id === id);
todo.done = !todo.done;
}Расширение .svelte.js говорит компилятору, что внутри есть руны. Модуль экспортирует объект состояния и функции, которые его меняют.
Использование в компонентах
Создайте счётчик для шапки:
<!-- src/lib/components/TodoCounter.svelte -->
<script>
import { todoStore } from '$lib/todos.svelte.js';
let remaining = $derived(todoStore.items.filter((t) => !t.done).length);
</script>
<span>Осталось: {remaining}</span>И выведите его рядом со списком:
<!-- src/routes/+page.svelte -->
<script>
import TodoCounter from '$lib/components/TodoCounter.svelte';
import { todoStore, addTodo, toggleTodo } from '$lib/todos.svelte.js';
</script>
<h1>Мои задачи <TodoCounter /></h1>
<ul>
{#each todoStore.items as todo (todo.id)}
<li>
<input type="checkbox" checked={todo.done} onchange={() => toggleTodo(todo.id)} />
{todo.text}
</li>
{/each}
</ul>
<button onclick={() => addTodo('Новая задача')}>Добавить</button>Отметьте задачу — счётчик в заголовке сразу уменьшится, хотя TodoCounter не получает никаких свойств. Оба компонента читают один и тот же объект.
Почему объект, а не массив
Экспортированную переменную нельзя переприсвоить из другого модуля: todoStore = ... в компоненте вызовет ошибку. Поэтому состояние оборачивают в объект и меняют его свойства: todoStore.items = [] работает.
Хранилища
До Svelte 5 общее состояние делали через хранилища (stores) из модуля svelte/store: функция writable создаёт хранилище, а в компоненте его значение читают с приставкой $ — $todos. Хранилища по-прежнему работают, и вы встретите их в старом коде и библиотеках. Для нового кода достаточно $state в файлах .svelte.js.
Осторожно с сервером
В SvelteKit модуль загружается на сервере один раз для всех посетителей. Если записывать в такое состояние данные конкретного пользователя во время серверной отрисовки, их увидят другие. Для списка задач в браузере это не страшно, а данные пользователей на сервере передают через функцию load и контекст.
Частая ошибка
Назвать файл todos.js без .svelte. Компилятор Svelte не обработает его, и при запуске вы получите ошибку, что $state не определён. Файлы с рунами всегда называйте *.svelte.js.