Головна / Статті / Svelte 5 Runes та SolidJS Signals: оновлення інтерфейсу без повторної рендеризації

Svelte 5 Runes та SolidJS Signals: оновлення інтерфейсу без повторної рендеризації

Дізнайтеся, як Svelte 5 компілює руни та SolidJS під’єднує сигнали для прямої зміни DOM, у чому це відрізняється від React та Angular, і коли варто змінити фреймворк.

1774 слів

Кожен розробник 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. Більше коду означає менше можливостей для помилок та швидші перевірки.
  • Малі пакети. Оскільки більша частина роботи виконується під час компіляції, витрати фреймворку в браузері є низькими. Для сайтів з контентом, лендінг-сторінок та будь-яких проектів, де важлива швидкість першого завантаження, це справжня бізнес-перевага.
  • Знайомі елементи створення веб-сайтів. Маркап, стилі та скрипти знаходяться в одному файлі та читаються як HTML, CSS та JavaScript. Не існує специфічних для JSX конвенцій, таких як 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 не мають аналогів, оскільки немає чого пропускати.
  • Прогнозовані ефекти. Ефект виконується тоді, коли змінюється сигнал, який він читає, а не тоді, коли компонент випадково переробляється та масив залежностей це дозволяє. Старі закриття, відома проблема React, у більшості випадків зникають, оскільки значення завжди читаються свіжими через геттери.
  • Плавний перехід від React. JSX та композиція компонентів передаються майже без змін, тому команді React здебільшого потрібно лише відмовитися від проблемних аспектів, а не заново вивчати все.
  • Звички, від яких потрібно відмовитися

    Модель одноразового виконання має наслідки, які створюють труднощі для початківців. Розбирання пропсів у верхній частині компонента зчитує їхні значення лише один раз, що порушує принцип реактивності, тому до пропсів зазвичай звертаються через props.name. Ранні повернення та умовні оператори в тілі функції оцінюються лише один раз, саме тому Solid надає компоненти керування потоком виконання, такі як Show та For. Як тільки ці правила стають зрозумілими, вони є послідовними, але саме вони є основною причиною помилок у розробників, які переходять з React.

    Як це порівнюється з React та Angular

    Глибша відмінність є філософською, а не синтаксичною.

    React створив „перезапуск та порівняння“ своєї основної моделі та протягом багатьох років додає інструменти для зниження витрат, пов’язаних із цією моделлю. Це все ще чудовий вибір: його екосистема є неперевершеною, найм співробітників відбувається легко, а React Compiler справді зменшує необхідність ручної мемоізації. Недолік полягає у тому, що ви працюєте в моделі, розробленій з урахуванням обмежень своєї епохи.

    Angular — це повнофункціональний варіант для корпоративного використання з можливостями вставки залежностей, RxJS та чіткими правилами для майже всього. Він добре справляється з дуже великими кодовими базами, але існують певні складнощі та необхідність тривалого навчання. Його найважливіші останні зміни — сигнали та беззонне виявлення змін — спрямовані на досягнення більш деталізованої реактивності, подібної до тієї, яку популяризував Solid.

    Ця конвергенція помітна в усій галузі. Angular прийняв концепцію сигналів, React впровадив компілятор на етапі збирання, новіші фреймворки, такі як Qwik, базуються на деталізованій реактивності, а Vue, чиї механізми реактивності завжди були близькі до сигналів, досліджує власні стратегії компіляції. Було б перебільшенням стверджувати, що Svelte та Solid винайшли кожну з цих ідей, але вони рано та чітко показали, що компіляція та сигнали можуть стати основою цілого фреймворку.

    Чому розробники продовжують рухатися в цьому напрямку

    Три не надто привабливі причини пояснюють більшу частину інтересу:

    • Менше інформації про фреймворк, яку потрібно запам’ятовувати. Увага зосереджується на продукті, а не на семантиці оновлень фреймворку. Правильне зберігання у пам’яті функцій-відповідей не вважається значущою роботою.
  • Висока продуктивність за замовчуванням. У React чи Angular швидкий додаток — це результат ретельної роботи інженерів. У Svelte чи Solid сповільнення додатку зазвичай вимагає значних зусиль.
  • Повага до вашого часу. Компактніші API, менше шаблонного коду та менше підводних каменів. Опитування розробників неодноразово високо оцінювали обидва фреймворки за рівнем задоволення та інтересу, при цьому їх загальне використання продовжує зростати на відносно невеликій основі.
  • Чи варто вам міняти?

    Для вже існуючого продукту майже напевно не негайно. Коли на React чи Angular вже працює значний за розміром додаток, і команда добре орієнтується в цьому стеку, його переписування є одним із найнадійніших способів затримати проект. Розмір екосистеми також має значення: у React є бібліотеки для майже будь-яких потреб, і хоча екосистеми Svelte та Solid є активними та розвиваються, вони менші, тому заздалегідь перевірте, чи існують та підтримуються компонентні бібліотеки, інструменти для форм та інтеграції систем автентифікації, від яких ви залежите.

    Підхід змінюється для нових проектів. Проект з нуля, віджет, чутливий до продуктивності, або внутрішній інструмент — це сфери з низьким ризиком для спроби використання нової технології:

    • виберіть Svelte, якщо хочете найпростіший шлях навчання та середовище, яке переважно просто функціонує
  • Виберіть Solid, якщо ваша команда працює з JSX та прагне максимальної продуктивності під час виконання з кодом, структурованим за принципами React.
  • Залишайтеся з React або Angular, коли широта екосистеми, можливості найму та наявні навички мають більше значення, ніж чиста ефективність.
  • Основні висновки

    • Модель рендерингу та порівняння в React є передбачуваною, але обчислення виконуються пропорційно до структури компонентів; мемоїзація та React Compiler зменшують обсяг цих обчислень, не змінюючи саму модель.
    • Svelte 5 переміщує механізм реактивності в компілятор за допомогою таких елементів, як $state та $derived, що дозволяє здійснювати прямі оновлення DOM та створює компактний движок виконання.
    • Solid виконує кожен компонент лише один раз та прямо пов’язує сигнали з вузлами DOM, що усуває необхідність повторного рендерингу, але вимагає нових підходів до роботи з параметрами та логікою керування.
    • Ширша екосистема рухається до спільних ідей: аналіз на етапі компіляції, сигнали замість повторного відрендерингу та прямі оновлення замість порівняння змін.
    • Використовуйте ці фреймворки там, де їхні переваги мають значення та де екосистема відповідає вашим потребам; не переписуйте здорову базу коду лише для того, щоб йти в ногу з трендом.