Контекст
Контекст позволяет компонентам получать доступ к значениям, принадлежащим родительским компонентам, без необходимости передавать их через пропсы (избегая передачи через множество промежуточных компонентов, что также называют «пробросом пропсов» (prop-drilling)).
Создав пару функций [get, set] с помощью createContext, вы можете установить контекст в родительском компоненте и получить его в дочернем:
<script> import Parent from './Parent.svelte'; import Child from './Child.svelte';</script>
<Parent> <Child /></Parent><script> import { setUserContext } from './context';
let { children } = $props();
setUserContext({ name: 'world' });</script>
{@render children()}<script> import { getUserContext } from './context';
const user = getUserContext();</script>
<h1>привет, {user.name}, внутри Child.svelte</h1>import { createContext } from 'svelte';
interface User { name: string;}
export const [getUserContext, setUserContext] = createContext<User>();<script lang="ts"> import Parent from './Parent.svelte'; import Child from './Child.svelte';</script>
<Parent> <Child /></Parent><script lang="ts"> import { setUserContext } from './context';
let { children } = $props();
setUserContext({ name: 'world' });</script>
{@render children()}<script lang="ts"> import { getUserContext } from './context';
const user = getUserContext();</script>
<h1>привет, {user.name}, внутри Child.svelte</h1>import { createContext } from 'svelte';
interface User { name: string;}
export const [getUserContext, setUserContext] = createContext<User>();Это особенно полезно, когда Parent.svelte не знает о Child.svelte напрямую, а вместо этого отображает его как часть сниппета children, как показано выше.
setContext и getContext
Заголовок раздела «setContext и getContext»В качестве альтернативы createContext вы можете использовать setContext и getContext напрямую. Родительский компонент устанавливает контекст с помощью setContext(key, value)…
<script> import { setContext } from 'svelte';
setContext('my-context', 'привет из Parent.svelte');</script><script lang="ts"> import { setContext } from 'svelte';
setContext('my-context', 'привет из Parent.svelte');</script>…а дочерний компонент получает его с помощью getContext.
<script> import { getContext } from 'svelte';
const message = getContext('my-context');</script>
<h1>{message}, внутри Child.svelte</h1><script lang="ts"> import { getContext } from 'svelte';
const message = getContext('my-context');</script>
<h1>{message}, внутри Child.svelte</h1>Ключ ('my-context' в примере выше) и сам контекст могут быть любыми значениями JavaScript.
Помимо setContext и getContext, Svelte предоставляет функции hasContext и getAllContexts.
Использование контекста с состоянием
Заголовок раздела «Использование контекста с состоянием»Вы можете хранить реактивное состояние в контексте…
<script> import { setCounter } from './context.ts'; import Child from './Child.svelte';
let counter = $state({ count: 0 });
setCounter(counter);</script>
<button onclick={() => counter.count += 1}> увеличить</button>
<Child /><Child /><Child />
<button onclick={() => counter.count = 0}> сбросить</button><script> import { getCounter } from './context.ts';
const counter = getCounter();</script>
<p>{counter.count}</p>import { createContext } from 'svelte';
interface Counter { count: number;}
export const [getCounter, setCounter] = createContext<Counter>();<script lang="ts"> import { setCounter } from './context.ts'; import Child from './Child.svelte';
let counter = $state({ count: 0 });
setCounter(counter);</script>
<button onclick={() => counter.count += 1}> увеличить</button>
<Child /><Child /><Child />
<button onclick={() => counter.count = 0}> сбросить</button><script lang="ts"> import { getCounter } from './context.ts';
const counter = getCounter();</script>
<p>{counter.count}</p>import { createContext } from 'svelte';
interface Counter { count: number;}
export const [getCounter, setCounter] = createContext<Counter>();…однако обратите внимание, что если вы переприсваиваете counter вместо его обновления, вы «разорвете связь» — другими словами, вместо этого…
<button onclick={() => counter = { count: 0 } }> сбросить</button>…вы должны делать так:
<button onclick={() => counter.count = 0}> сбросить</button>Svelte предупредит вас, если вы сделаете что-то неправильно.
Тестирование компонентов
Заголовок раздела «Тестирование компонентов»При написании компонентных тестов бывает полезно создать обёрточный компонент, который устанавливает контекст, чтобы проверить поведение компонента, который этот контекст использует. Начиная с версии 5.49, можно делать примерно следующее:
import { mount, unmount } from 'svelte';import { expect, test } from 'vitest';import { setUserContext } from './context';import MyComponent from './MyComponent.svelte';
test('MyComponent', () => { function Wrapper(...args) { setUserContext({ name: 'Вован' }); return MyComponent(...args); }
const component = mount(Wrapper, { target: document.body });
expect(document.body.innerHTML).toBe('<h1>Привет, Вован!</h1>');
unmount(component);});Этот подход также работает с hydrate и render.
Замена глобального состояния
Заголовок раздела «Замена глобального состояния»Когда у вас есть состояние, которое используется многими компонентами, может возникнуть соблазн поместить его в отдельный модуль и просто импортировать там, где это необходимо:
export const myGlobalState = $state({ user: { // ... } // ...});Во многих случаях это вполне допустимо, но есть риск: если вы изменяете состояние во время рендеринга на стороне сервера (что не рекомендуется, но всё же возможно)…
<script> import { myGlobalState } from 'svelte';
let { data } = $props();
if (data.user) { myGlobalState.user = data.user; }</script><script lang="ts"> import { myGlobalState } from 'svelte';
let { data } = $props();
if (data.user) { myGlobalState.user = data.user; }</script>…то данные могут стать доступными для следующего пользователя. Контекст решает эту проблему, так как он не является общим между запросами.