Контекст
Контекст позволяет компонентам получать доступ к значениям, принадлежащим родительским компонентам, без необходимости передавать их через пропсы (избегая передачи через множество промежуточных компонентов, что также называют «пробросом пропсов» («prop-drilling»)). Родительский компонент устанавливает контекст с помощью setContext(key, value)
…
// Parent.svelte
<script>
import { setContext } from 'svelte';
setContext('my-context', 'привет из Parent.svelte');
</script>
// Parent.svelte
<script lang="ts">
import { setContext } from 'svelte';
setContext('my-context', 'привет из Parent.svelte');
</script>
…а дочерний компонент получает его с помощью getContext
.
// Child.svelte
<script>
import { getContext } from 'svelte';
const message = getContext('my-context');
</script>
<h1>{message}, внутри Child.svelte</h1>
// Child.svelte
<script lang="ts">
import { getContext } from 'svelte';
const message = getContext('my-context');
</script>
<h1>{message}, внутри Child.svelte</h1>
Это особенно полезно, когда Parent.svelte
не знает напрямую о Child.svelte
, а вместо этого отображает его как часть сниппета children
(демонстрация).
<Parent>
<Child />
</Parent>
Ключ ('my-context'
в примере выше) и сам контекст могут быть любыми значениями JavaScript.
Помимо setContext
и getContext
, Svelte предоставляет функции hasContext
и getAllContexts
.
Использование контекста с состоянием
Вы можете хранить реактивное состояние в контексте (демонстрация)…
<script>
import { setContext } from 'svelte';
import Child from './Child.svelte';
let counter = $state({
count: 0
});
setContext('counter', counter);
</script>
<button onclick={() => counter.count += 1}>
увеличить
</button>
<Child />
<Child />
<Child />
…однако обратите внимание, что если вы переприсваиваете counter
вместо его обновления, вы «разорвете связь» — другими словами, вместо этого…
<button onclick={() => counter = { count: 0 }}>
сбросить
</button>
…вы должны делать так:
<button onclick={() => counter.count = 0}>
сбросить
</button>
Svelte предупредит вас, если вы сделаете что-то неправильно.
Типобезопасный контекст
Полезным шаблоном является оборачивание вызовов setContext
и getContext
внутри вспомогательных функций, которые позволяют сохранить типобезопасность:
// context.js
import { getContext, setContext } from 'svelte';
let key = {};
/** @param {User} user */
export function setUserContext(user) {
setContext(key, user);
}
export function getUserContext() {
return /** @type {User} */ (getContext(key));
}
// context.ts
import { getContext, setContext } from 'svelte';
let key = {};
export function setUserContext(user: User) {
setContext(key, user);
}
export function getUserContext() {
return getContext(key) as User;
}
Замена глобального состояния
Когда у вас есть состояние, которое используется многими компонентами, может возникнуть соблазн поместить его в отдельный модуль и просто импортировать там, где это необходимо:
// state.svelte.js
export const myGlobalState = $state({
user: {
// ...
}
// ...
});
Во многих случаях это вполне допустимо, но есть риск: если вы изменяете состояние во время рендеринга на стороне сервера (что не рекомендуется, но всё же возможно)…
// App.svelte
<script>
import { myGlobalState } from 'svelte';
let { data } = $props();
if (data.user) {
myGlobalState.user = data.user;
}
</script>
// App.svelte
<script lang="ts">
import { myGlobalState } from 'svelte';
let { data } = $props();
if (data.user) {
myGlobalState.user = data.user;
}
</script>
…то данные могут стать доступными для следующего пользователя. Контекст решает эту проблему, так как он не является общим между запросами.