Svelte 5 Runes та SolidJS Signals: оновлення інтерфейсу без повторної рендеризації
Дізнайтеся, як Svelte 5 компілює руни та SolidJS під’єднує сигнали для прямої зміни DOM, у чому це відрізняється від React та Angular, і коли варто змінити фреймворк.
Кожен розробник React рано чи пізно змушений пояснювати, чому компонент відображається чотири рази, коли нічого видимого не змінюється, та чому для виправлення цього проблеми потрібні memo, масив залежностей та стабільний керуючий функція. Svelte та SolidJS виходять з іншої передумови: якщо фреймворк точно знає, яка частина стану впливає на певний елемент DOM, він може оновити лише цей елемент та зовсім уникнути повторної обробки компонентів. У цій статті пояснюється, як кожен з цих фреймворків досягає цього, як виглядає код на практиці, у чому їхня модель відрізняється від React та Angular, та як вирішити, чи підходить хоч один з них для вашого наступного проекту.
Віртуальний DOM був засобом, а не метою
Ключовою ідеєю React під час його запуску у 2013 році був віртуальний DOM. Інтерфейс описується як функція від стану. Коли стан змінюється, React знову викликає ваш компонент, створює нове дерево в пам’яті, порівнює його з попереднім та застосовує лише відмінності до реального DOM.
Ця архітектура робить інтерфейси набагато прогнозованішими, ніж ручна маніпуляція DOM, і залишається ефективною моделлю. Однак у неї є й недоліки. Функція компонента виконується знову незалежно від того, чи потрібні зміни у її результаті, виділяється нове дерево, і виконується процес порівняння для визначення того, що саме змінилося — у найгіршому випадку для оновлення одного <span>. Якщо ви хочете дізнатися деталі про те, що саме порівнює механізм узгодження та чому, стаття про те, як працює порівняння віртуального DOM розглядає це питання.
Більша частина API React з того часу — memo, useMemo, useCallback, а також нещодавно з’явлений React Compiler — існує для того, щоб уникнути обробки даних, яку інакше виконував би механізм рендерингу та порівняння. Компілятор автоматизує процес мемоізації, тож розробникам доводиться менше писати код вручну, але основна логіка залишається незмінною: компоненти знову виконуються, а оптимізація полягає у тому, щоб переконати їх цього не робити. Стаття на тему того, що оптимізує React Compiler та що залишає для вас детально розглядає ці обмеження.
Svelte та Solid ставлять простіше запитання: а що, якби залежність між кожною частиною стану та кожним вузлом DOM була точно відома, щоб при зміні стану оброблявся лише відповідний вузол?
Svelte: компілятор, який пише код оновлення за вас
Svelte — це насамперед компілятор. Ви створюєте компоненти у файлах з розширенням .svelte, і під час збирання Svelte перетворює їх на звичайний JavaScript, який безпосередньо маніпулює DOM. Не існує віртуального DOM та процесу порівняння під час виконання, і до браузера передається лише невеликий набір засобів для виконання.
З Svelte 5 реактивність виражається через runes — спеціальні примітиви, які розпізнає компілятор. У наведеному нижче компоненті оголошується стан та значення, отримане з нього, після чого обидва відображаються у формі кнопки:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{count} doubled is {doubled}
</button>
Це весь компонент. Немає функції-встановлювача та немає масиву залежностей. Ви збільшуєте значення count, ніби це звичайна змінна, і оскільки компілятор вже проаналізував, які частини маркування використовують count та doubled, він генерує код, який оновлює саме ці текстові вузли. $derived перераховується лише тоді, коли змінюється щось із того, що він читає. Зауважте, що обробники подій у Svelte 5 — це звичайні атрибути, такі як onclick, що замінюють стару синтаксис-директиву on:click.
Чому команди його люблять
- Більше коду для однакового результату. Без хуків, функцій-встановлювачів чи компонентів-обгорток компоненти Svelte зазвичай виходять значно коротшими за свої аналоги у React. Більше коду означає менше можливостей для помилок та швидші перевірки.
className, що робить ці файли зрозумілими для дизайнерів та рецензентів, які не пишуть на React.Для повноцінних додатків SvelteKit додає механізми маршрутизації, серверне рендеринг та API-кінцеві точки, виконуючи ту саму функцію, що й Next.js для React, причому він славиться простішою налаштуванням.
Є одна важлива умова, про яку варто знати заздалегідь: руни є функціями компілятора, тому вони працюють лише всередині файлів .svelte та в модулях із розширеннями .svelte.js або .svelte.ts. Перенесення реактивної логіки до звичайного файлу утиліт .js не спрацює без такого найменування.
SolidJS: JSX, який виконується один раз
На перший погляд Solid легко сплутати з React. Він використовує JSX, складає невеликі функції, а кількісний показник виглядає майже ідентично:
function Counter() {
const [count, setCount] = createSignal(0);
return (
<button onClick={() => setCount(count() + 1)}>
Count: {count()}
</button>
);
}
Різниця, яка дивує розробників React, полягає у тому, що Counter виконується рівно один раз. Solid побудований на дрібногранній реактивності за допомогою сигналів. createSignal повертає геттер та сеттер, причому геттер count() є викликом функції, а не звичайною значенням. Коли JSX читає count() у виразі, Solid фіксує, що цей конкретний текстовий вузол залежить від цього сигналу. Подальший виклик setCount оновлює лише цей текстовий вузол, а не щось інше. Функція компонента була лише кроком налаштування, який під’єднував сигнали до вузлів DOM; вона більше ніколи не виконується, тому немає чого перерендерувати.
Саме завдяки цій моделі Solid досягає результатів на рівні або близько до найкращих у тестах поширених фреймворків, часто наближаючись до результатів ручно написаного стандартного JavaScript. Розглядайте будь-які рейтинги тестів як моментальний знімок та вимірюйте свою власну навантаженість, проте архітектурна перевага є реальною: оновлення коштують приблизно пропорційно тому, що змінилося, а не до розміру дерева компонентів.
Чому команди його люблять
- Відсутня необхідність у повторному відрендеруванні. Не виникає питання, чому щось було відрендеровано, як у React. Функції
useMemo,useCallbackтаReact.memoне мають аналогів, оскільки немає чого пропускати.
Звички, від яких потрібно відмовитися
Модель одноразового виконання має наслідки, які створюють труднощі для початківців. Розбирання пропсів у верхній частині компонента зчитує їхні значення лише один раз, що порушує принцип реактивності, тому до пропсів зазвичай звертаються через props.name. Ранні повернення та умовні оператори в тілі функції оцінюються лише один раз, саме тому Solid надає компоненти керування потоком виконання, такі як Show та For. Як тільки ці правила стають зрозумілими, вони є послідовними, але саме вони є основною причиною помилок у розробників, які переходять з React.
Як це порівнюється з React та Angular
Глибша відмінність є філософською, а не синтаксичною.
React створив „перезапуск та порівняння“ своєї основної моделі та протягом багатьох років додає інструменти для зниження витрат, пов’язаних із цією моделлю. Це все ще чудовий вибір: його екосистема є неперевершеною, найм співробітників відбувається легко, а React Compiler справді зменшує необхідність ручної мемоізації. Недолік полягає у тому, що ви працюєте в моделі, розробленій з урахуванням обмежень своєї епохи.
Angular — це повнофункціональний варіант для корпоративного використання з можливостями вставки залежностей, RxJS та чіткими правилами для майже всього. Він добре справляється з дуже великими кодовими базами, але існують певні складнощі та необхідність тривалого навчання. Його найважливіші останні зміни — сигнали та беззонне виявлення змін — спрямовані на досягнення більш деталізованої реактивності, подібної до тієї, яку популяризував Solid.
Ця конвергенція помітна в усій галузі. Angular прийняв концепцію сигналів, React впровадив компілятор на етапі збирання, новіші фреймворки, такі як Qwik, базуються на деталізованій реактивності, а Vue, чиї механізми реактивності завжди були близькі до сигналів, досліджує власні стратегії компіляції. Було б перебільшенням стверджувати, що Svelte та Solid винайшли кожну з цих ідей, але вони рано та чітко показали, що компіляція та сигнали можуть стати основою цілого фреймворку.
Чому розробники продовжують рухатися в цьому напрямку
Три не надто привабливі причини пояснюють більшу частину інтересу:
- Менше інформації про фреймворк, яку потрібно запам’ятовувати. Увага зосереджується на продукті, а не на семантиці оновлень фреймворку. Правильне зберігання у пам’яті функцій-відповідей не вважається значущою роботою.
Чи варто вам міняти?
Для вже існуючого продукту майже напевно не негайно. Коли на React чи Angular вже працює значний за розміром додаток, і команда добре орієнтується в цьому стеку, його переписування є одним із найнадійніших способів затримати проект. Розмір екосистеми також має значення: у React є бібліотеки для майже будь-яких потреб, і хоча екосистеми Svelte та Solid є активними та розвиваються, вони менші, тому заздалегідь перевірте, чи існують та підтримуються компонентні бібліотеки, інструменти для форм та інтеграції систем автентифікації, від яких ви залежите.
Підхід змінюється для нових проектів. Проект з нуля, віджет, чутливий до продуктивності, або внутрішній інструмент — це сфери з низьким ризиком для спроби використання нової технології:
- виберіть Svelte, якщо хочете найпростіший шлях навчання та середовище, яке переважно просто функціонує
Основні висновки
- Модель рендерингу та порівняння в React є передбачуваною, але обчислення виконуються пропорційно до структури компонентів; мемоїзація та React Compiler зменшують обсяг цих обчислень, не змінюючи саму модель.
- Svelte 5 переміщує механізм реактивності в компілятор за допомогою таких елементів, як
$stateта$derived, що дозволяє здійснювати прямі оновлення DOM та створює компактний движок виконання. - Solid виконує кожен компонент лише один раз та прямо пов’язує сигнали з вузлами DOM, що усуває необхідність повторного рендерингу, але вимагає нових підходів до роботи з параметрами та логікою керування.
- Ширша екосистема рухається до спільних ідей: аналіз на етапі компіляції, сигнали замість повторного відрендерингу та прямі оновлення замість порівняння змін.
- Використовуйте ці фреймворки там, де їхні переваги мають значення та де екосистема відповідає вашим потребам; не переписуйте здорову базу коду лише для того, щоб йти в ногу з трендом.