Как работает система реактивности в Svelte? Что такое реактивное объявление $:?
Svelte компилирует реактивность в прямые DOM-вызовы без Virtual DOM. Метка $: создаёт реактивное выражение: оно автоматически перевычисляется при изменении любой используемой переменной.
Система реактивности в Svelte
Svelte переносит реактивность на этап компиляции. Компилятор анализирует .svelte-файл и генерирует JavaScript, который точечно обновляет DOM при изменении состояния — без diff-алгоритма и Virtual DOM. Каждое присваивание переменной превращается в вызов $$invalidate(), который помечает компонент «грязным» и планирует микро-задачу на обновление.
Как работает $:
Метка $: — стандартный синтаксис JavaScript (labelled statement), переосмысленный Svelte. Компилятор обнаруживает её и превращает в реактивную зависимость: выражение или блок перевычисляется каждый раз, когда меняется хотя бы одна из используемых реактивных переменных.
<script>
let width = 100;
let height = 50;
// реактивное объявление: пересчитывается при изменении width или height
$: area = width * height;
// реактивный блок: побочный эффект
$: {
console.log(`Размеры: ${width} x ${height}`);
document.title = `Площадь: ${area}`;
}
// реактивный if
$: if (area > 10000) {
console.warn('Слишком большая площадь!');
}
</script>
<input type="number" bind:value={width} />
<input type="number" bind:value={height} />
<p>Площадь: {area}</p>
Что генерирует компилятор
Упрощённый вид сгенерированного кода для $: area = width * height:
// В функции обновления компонента:
if (dirty & /*width*/ 1 || dirty & /*height*/ 2) {
ctx[2] = /*area*/ ctx[0] * ctx[1]; // area = width * height
}
Компилятор строит граф зависимостей статически: он знает, что area зависит от width и height, и генерирует минимальный код проверки.
Порядок выполнения реактивных операторов
Svelte топологически сортирует $:-объявления перед генерацией кода, поэтому зависимые выражения всегда выполняются в правильном порядке:
<script>
let x = 2;
$: doubled = x * 2; // зависит от x
$: quadrupled = doubled * 2; // зависит от doubled — выполнится после
</script>
Подводные камни
- Объекты и массивы не триггерят реактивность мутацией.
arr.push(item)не вызовет обновление — нужно присваивание:arr = [...arr, item]. - Деструктурирование теряет реактивность.
const { a } = obj—aне реактивна; используйтеobj.aнапрямую или$: ({ a } = obj). - $: выполняется не сразу при объявлении, а после первого рендера — не полагайтесь на него для инициализации синхронного состояния.
- Циклические зависимости в
$:-блоках приведут к бесконечному перевычислению. Компилятор не всегда их обнаруживает. - Переменные из закрытых модулей (импорты) не отслеживаются автоматически — только локальные переменные
letкомпонента. - В Svelte 5
$:устарел — вместо него используются руны$derivedи$effect. Смешивание двух моделей в одном проекте создаёт путаницу. - Реактивные блоки с побочными эффектами выполняются синхронно в очереди обновлений — тяжёлые операции (fetch, DOM-манипуляции) лучше вынести в
afterUpdate.
Common mistakes
- Путать legacy $: reactivity с похожим API из соседнего фреймворка.
- Не объяснять, где код выполняется: сервер, клиент, build step или runtime.
- Игнорировать влияние на hydration, cache, bundle size или безопасность.
What the interviewer is testing
- Точно объясняет назначение механизма «legacy $: reactivity».
- Показывает корректный минимальный пример без выдуманных API.
- Называет ограничения, failure modes и production-компромиссы.