Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Общее состояние в файлах .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.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.