$state
Руна $state
позволяет вам создавать реактивное состояние, что означает, что ваш пользовательский интерфейс реагирует на изменения.
<script> let count = $state(0);</script>
<button onclick={() => count++}> нажатий: {count}</button>
В отличие от других фреймворков, с которыми вы могли столкнуться, нет API для взаимодействия с состоянием — count
просто число, а не объект или функция, и вы можете обновлять его так же, как обновляете любую другую переменную.
Глубокое состояние
Заголовок раздела «Глубокое состояние»Если $state
используется с массивом или простым объектом, результатом будет глубоко реактивный прокси состояния. Объекты Прокси позволяют Svelte выполнять код при чтении или записи свойств, включая методы, такие как array.push(...)
, что вызывает детализированные обновления.
Состояние проксируется рекурсивно, пока Svelte не встретит что-то, отличное от массива или простого объекта (например, класс). В таком случае…
let todos = $state([ { done: false, text: 'добавить задачу' }]);
…изменение свойства отдельной задачи вызовет обновление всего в вашем пользовательском интерфейсе, что зависит от этого конкретного свойства:
todos[0].done = !todos[0].done;
Если вы добавите новый объект в массив, он также будет проксирован:
todos.push({ done: false, text: 'пообедать'});
Обратите внимание, что если вы деструктурируете реактивное значение, ссылки не являются реактивными — как и в обычном JavaScript, они анализируются в момент деструктуризации:
let { done, text } = todos[0];
// это не повлияет на значение `done`todos[0].done = !todos[0].done;
Экземпляры классов не проксируются. Вместо этого вы можете использовать $state
в полях класса (как публичных, так и приватных) или в качестве первого присваивания свойства непосредственно внутри конструктора:
class Todo { done = $state(false);
constructor(text) { this.text = $state(text); }
reset() { this.text = ''; this.done = false; }}
При вызове методов в JavaScript значение this
имеет значение. Это не сработает, потому что this
внутри метода reset
будет ссылаться на <button>
, а не на Todo
:
<button onclick={todo.reset}> reset</button>
Вы можете использовать либо встроенную функцию…
<button onclick={() => todo.reset()}> reset</button>
…либо стрелочную функцию в определении класса:
class Todo { done = $state(false); text = $state();
constructor(text) { this.text = text; }
reset = () => { this.text = ''; this.done = false; }}
Svelte предоставляет реактивные реализации встроенных классов, таких как
Set
иMap
, которые можно импортировать изsvelte/reactivity
.
$state.raw
Заголовок раздела «$state.raw»В случаях, когда вы не хотите, чтобы объекты и массивы были глубоко реактивными, вы можете использовать $state.raw
.
Состояние, объявленное с помощью $state.raw
, не может быть мутировано; его можно только переопределить. Другими словами, вместо того чтобы присваивать значение свойству объекта или использовать метод массива, такой как push
, замените объект или массив целиком, если хотите его обновить:
let person = $state.raw({ name: 'Heraclitus', age: 49});
// это не окажет никакого эффектаperson.age += 1;
// это сработает, потому что мы создаем новый объектperson = { name: 'Heraclitus', age: 50};
Это может улучшить производительность при работе с большими массивами и объектами, которые вы не планировали мутировать, поскольку это позволяет избежать затрат на их реактивность. Обратите внимание, что исходное состояние может содержать реактивное состояние (например, исходный массив реактивных объектов).
Как и в случае со $state
, вы можете объявлять поля класса, используя $state.raw
.
$state.snapshot
Заголовок раздела «$state.snapshot»Чтобы сделать статический снимок глубоко реактивного прокси $state
, используйте $state.snapshot
:
<script> let counter = $state({ count: 0 });
function onclick() { // Будет выведено `{ count: ... }`, а не `Proxy { ... }` console.log($state.snapshot(counter)); }</script>
Это удобно, когда вы хотите передать состояние во внешнюю библиотеку или API, которые не ожидают прокси, такие как structuredClone
.
Передача состояния в функции
Заголовок раздела «Передача состояния в функции»JavaScript — это язык с передачей по значению — когда вы вызываете функцию, аргументы являются значениями, а не переменными. Другими словами:
/** * @param {number} a * @param {number} b */function add(a, b) { return a + b;}
let a = 1;let b = 2;let total = add(a, b);console.log(total); // 3
a = 3;b = 4;console.log(total); // всё ещё 3!
function add(a: number, b: number) { return a + b;}
let a = 1;let b = 2;let total = add(a, b);console.log(total); // 3
a = 3;b = 4;console.log(total); // всё ещё 3!
Если add
хочет получить доступ к текущим значениям a
и b
, а также вернуть текущее значение total
, вам нужно использовать функции вместо этого:
/** * @param {() => number} getA * @param {() => number} getB */function add(getA, getB) { return () => getA() + getB();}
let a = 1;let b = 2;let total = add(() => a, () => b);console.log(total()); // 3
a = 3;b = 4;console.log(total()); // 7
function add(getA: () => number, getB: () => number) { return () => getA() + getB();}
let a = 1;let b = 2;let total = add(() => a, () => b);console.log(total()); // 3
a = 3;b = 4;console.log(total()); // 7
Состояние в Svelte ничем не отличается — когда вы ссылаетесь на что-то, объявленное с помощью руны $state
…
let a = $state(1);let b = $state(2);
…вы получаете его текущее значение.
Обратите внимание, что «функции» — это широкое понятие — оно охватывает свойства прокси и свойства get
/set
…
/** * @param {{ a: number, b: number }} input */function add(input) { return { get value() { return input.a + input.b; } };}
let input = $state({ a: 1, b: 2 });let total = add(input);console.log(total.value); // 3
input.a = 3;input.b = 4;console.log(total.value); // 7
function add(input: { a: number, b: number }) { return { get value() { return input.a + input.b; } };}
let input = $state({ a: 1, b: 2 });let total = add(input);console.log(total.value); // 3
input.a = 3;input.b = 4;console.log(total.value); // 7
…хотя если вы обнаружите, что пишете код подобным образом, подумайте о том, чтобы перейти на классы.
Передача состояния между модулями
Заголовок раздела «Передача состояния между модулями»Вы можете объявлять состояние в файлах .svelte.js
и .svelte.ts
, но экспортировать это состояние можно только если оно не переназначается напрямую. Другими словами, так делать нельзя:
export let count = $state(0);
export function increment() { count += 1;}
Это происходит потому, что все ссылки на count
преобразуются компилятором Svelte — приведённый выше код примерно эквивалентен следующему:
export let count = $.state(0);
export function increment() { $.set(count, $.get(count) + 1);}
Поскольку компилятор обрабатывает только один файл за раз, если другой файл импортирует count
, Svelte не знает, что нужно обернуть каждое обращение в $.get
и $.set
:
import { count } from './state.svelte.js';
console.log(typeof count); // 'object', не 'number'
Это оставляет вам два варианта для совместного использования состояния между модулями — либо не переназначать его…
// Это разрешено — поскольку мы обновляем// `counter.count`, а не сам `counter`,// Svelte не оборачивает его в `$.state`export const counter = $state({ count: 0});
export function increment() { counter.count += 1;}
…либо не экспортировать его напрямую:
let count = $state(0);
export function getCount() { return count;}
export function increment() { count += 1;}