Контекст: setContext и getContext
Контекст передаёт данные вниз по дереву компонентов без прокидывания пропсов через каждый уровень.
«Pror-drilling — это когда пропс едет через десять компонентов, нужный только последнему.» Контекст решает эту боль.
Иногда данные нужны глубоко вложенному компоненту, но передавать их пропсами через все промежуточные уровни утомительно и засоряет код — это называют prop-drilling. Svelte предлагает контекст: setContext(key, value) в компоненте-предке делает значение доступным всем потомкам, а getContext(key) в потомке его читает. Промежуточные компоненты ничего не знают об этих данных.
Важное отличие контекста от общего модуля: контекст привязан к дереву компонентов и к конкретному рендеру. Это делает его безопасным для серверного рендеринга — у каждого запроса своё дерево, и данные пользователей не смешиваются. Поэтому пользовательские данные (текущий юзер, тема) на сервере лучше класть в контекст, а не в глобальный модуль.
<!-- Layout.svelte (предок) -->
<script>
import { setContext } from 'svelte';
let theme = $state('dark');
setContext('theme', { get value() { return theme; }, toggle() { theme = theme === 'dark' ? 'light' : 'dark'; } });
</script><!-- глубоко вложенный потомок -->
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<button onclick={theme.toggle}>Тема: {theme.value}</button>Чтобы контекст оставался реактивным, передавайте не голое значение, а объект с геттером (как выше), либо стор. Тогда потомок будет видеть актуальное состояние, а не снимок на момент setContext.
Как это работает под капотом
Контекст — это словарь «ключ → значение», прикреплённый к узлу дерева. При поиске потомок поднимается вверх по цепочке предков, пока не найдёт ключ. Смоделируем поиск по цепочке.
// Контекст: поиск значения вверх по дереву предков
function createNode(parent) {
return { parent, context: new Map(),
setContext(k, v) { this.context.set(k, v); },
getContext(k) {
let node = this;
while (node) { if (node.context.has(k)) return node.context.get(k); node = node.parent; }
return undefined;
}
};
}
const layout = createNode(null);
layout.setContext('theme', 'dark');
const page = createNode(layout);
const deepButton = createNode(page); // глубоко вложен
console.log('кнопка нашла тему:', deepButton.getContext('theme')); // dark (поднялась к layout)Попробуй сам ▶ — вставь код в консоль браузера (F12 → Console) и нажми Enter, чтобы увидеть вывод.
Layout setContext('theme', ...)
|
v
Page (ничего не знает о theme)
|
v
DeepButton getContext('theme') -> поднимается вверх -> находит у LayoutЧастые ошибки
- Ждать реактивности от голого значения в контексте. Передавайте объект с геттером или стор.
- Вызывать
getContextвне инициализации компонента. Он работает при создании, а не в произвольный момент. - Хранить данные пользователя в глобальном модуле на сервере. Это утечёт между запросами; используйте контекст.
Best practices
- Используйте контекст для сквозных вещей: тема, локаль, текущий пользователь.
- Передавайте реактивный объект-обёртку, а не снимок значения.
- Задавайте ключи как уникальные строки или символы, чтобы избежать коллизий.
Контекст против пропсов и глобального состояния
У контекста есть чёткая ниша между двумя соседними инструментами, и важно её не размывать. От обычных пропсов контекст отличается тем, что не требует прокидывания значения через каждый промежуточный уровень — он удобен для сквозных вещей, нужных глубоко и повсеместно: тема, локаль, текущий пользователь, конфигурация. От глобального модуля контекст отличается привязкой к дереву компонентов и к конкретному рендеру, что делает его безопасным на сервере: у каждого запроса своё дерево, и данные пользователей не смешиваются. Отсюда практическое правило для SvelteKit: пользовательские данные на сервере передавайте через контекст, а не через глобальный модуль. При этом не превращайте контекст в свалку — если значение нужно лишь паре соседних компонентов, проще передать его пропсом. И не забывайте про реактивность: кладите в контекст не голый снимок, а объект с геттером или стор, иначе потомки увидят значение, застывшее на момент вызова setContext.
Итог: контекст (setContext/getContext) передаёт данные вниз по дереву без prop-drilling и безопасен при серверном рендеринге. Для реактивности оборачивайте значение в объект с геттером.