Фото: Karsten Winegeart на Unsplash
Сигнали (Signals) пропонують численні переваги, включаючи вбудовану гранулярну реактивність, простий API, лаконічний і зрозумілий потік даних та багато іншого.
Саме тому вони використовуються в майже всіх основних фреймворках, таких як Solid, Qwix, Angular та Vue.
Вони стали настільки популярними, що наразі існує пропозиція додати їх безпосередньо в мову JavaScript:
[
GitHub - tc39/proposal-signals: Пропозиція додати сигнали до JavaScript.
Пропозиція додати сигнали до JavaScript. Внесіть свій вклад у розробку tc39/proposal-signals, створивши обліковий запис на…
github.com
](https://github.com/tc39/proposal-signals?source=post_page-----19cbcb6b802b--------------------------------)
Сигнали — це чудово, але як саме вони працюють за лаштунками?
В цій статті я використаю синтаксис SolidJs, але концепції застосовні до всіх фреймворків, які використовують сигнали.
Я також поясню спрощену версію реальних реалізацій.
Давайте почнемо з вивчення найпростішої одиниці у світі сигналів: Signal (Сигнал).
Signal (Сигнал)
Signal (Сигнал) — це реактивна структура даних, яка використовується для керування станом в нашій програмі.
Давайте створимо Signal!
Ми створюємо коробку з значенням (в цьому випадку "John") і списком підписників.
Також ми повертаємо аксесор (в цьому випадку name
) і сеттер (setName
).
Аксесори (Accessors) надають спосіб отримувати дані всередині сигналу; ми не можемо безпосередньо отримати доступ до значення.
Сеттер (Setter) використовується для оновлення даних у Signal, оскільки безпосередній доступ заборонено.
Чудово, у нас є спосіб зберігати стан, але нам також потрібен спосіб реагувати на зміни цього стану. Для цього ми можемо використовувати Effect (Ефект).
Effect (Ефект)
Effect (Ефект) — це наш інструмент для реакції на зміни в сигналах.
Давайте використаємо один і подивимося, як це працює.
Ми створили дуже базовий Effect (Ефект), який викликає аксесор (accessor) name
і виводить результат в консоль.
Ось наш маленький Effect (Ефект), що містить анонімну функцію.
Щоб зрозуміти, що відбувається, коли ми створюємо Effect (Ефект), нам потрібно ввести контекст відслідковування (tracking context).
Контекст відслідковування — це глобальний стек виконання, який допомагає відслідковувати те, що наразі виконується.
Коли ми створюємо Effect (Ефект), ми додаємо його в контекст відслідковування:
Як тільки ми додаємо Effect (Ефект) в контекст відслідковування, ми виконуємо функцію, яку він містить.
Чарівність відбувається, коли ми виконуємо будь-який Accessor (Аксесор) всередині createEffect
.
Коли ми викликаємо Accessor (Аксесор), ми перевіряємо верхівку контексту відслідковування.
Тепер ми додаємо Effect (Ефект) на верхівку контексту відслідковування як підписника (subscriber) сигналу, який підключений до аксесора (accessor).
(Жовта зірочка позначає Effect (Ефект)).
Після того, як ми додали Effect (Ефект) як підписника, ми повертаємо значення, яке утримує Signal (Сигнал), в даному випадку — John
.
Тоді ми виводимо John
в консоль.
І видаляємо Effect (Ефект) з контексту відслідковування:
Отже, ми вивели в консоль.
У чому ж полягає основна ідея? Щоб зрозуміти це, нам потрібно викликати сеттер setName
.
Виклик сеттера
При виклику Setter (Сеттера) ми оновлюємо значення, яке утримує Signal (Сигнал).
Далі ми перевіряємо список підписників (subscribers), видаляємо їх зі списку і викликаємо кожного.
Ми заміняємо значення всередині Signal (Сигнал) на "Jane"
, видаляємо Effect (Ефект) зі списку і викликаємо його.
У цьому випадку цей Effect (Ефект):
Це, в свою чергу, виводить "Jane"
і повторює процес реєстрації себе як підписника оригінального сигналу.
Ми маємо реактивну систему, яка відповідає на зміни стану.
Легко побачити, як ця система може бути використана для детальних оновлень UI, фактично створюючи Effect (Ефект), який рендерить компонент лише коли змінюється конкретний Signal (Сигнал).
Ми також можемо побачити, як ця система може масштабуватись, коли більше компонентів підписуються на більше Signals (Сигнали).
Усі ці переваги приходять без додаткових зусиль з боку розробника — підписки обробляються автоматично за лаштунками.
Одним словом: неймовірно!
Фото: Nahima Aparicio на Unsplash
Мемоізація (Memoization)
Якщо б я попросив вас знайти добуток 12 * 16, вам, ймовірно, довелося б трохи поміркувати — можливо, подвоїти 6 * 2, а потім додати числа, і так далі.
Але якщо я спитаю вас знову, ви, ймовірно, одразу скажете 192 без зайвих зусиль.
Іноді корисно запам'ятовувати обчислення, які ми вже зробили, замість того, щоб виконувати їх повторно.
Ця логіка стосується нас, і вона також застосовується до Signals (Сигналів).
Ми можемо використовувати функцію createMemo
для реалізації цього патерну з Signals (Сигналами).
Що відбувається за лаштунками, коли ми викликаємо функцію createMemo
?
По-перше, ми створюємо Memo (Мемо), що містить масив підписників, подібно до того, як ми працюємо з Signals (Сигналами).
Далі ми розміщаємо спеціальну обгорткову функцію M: f()
, в контексті відслідковування, так само, як ми робили з Effect (Ефект).
Після цього ми викликаємо функцію, передану як вхід до createMemo
.
Коли функція виконується, вона викликає num()
, роблячи M: f()
підписником (subscriber) сигналу.
Далі ми зберігаємо результат у Memo (Мемо).
Це, по суті, означає, що ми можемо завжди повернути збережене значення і уникнути повторних обчислень, поки сигнал, на який ми підписані, не зміниться.
Давайте подивимося, що відбудеться, коли ми використаємо memo()
(значення, яке повертає createMemo
).
Ми створили ефект, який викликає функцію memo
.
І знову ми бачимо ту саму схему, що й з аксесор-функцією сигналу — коли її викликають, ми перевіряємо верхівку контексту відслідковування та реєструємо Effect (Ефект) як підписника до Memo.
Далі ми виводимо effect: 2
, використовуючи мемоізоване значення без повторного обчислення — великий успіх!
Давайте подивимося, як усе змінюється, коли ми змінимо значення сигналу.
Після зміни значення сигналу на 2, ми викликаємо всіх підписників — в цьому випадку обгорткову мемо-функцію.
Повторний виклик функції призводить до наступного:
- Обгорткова функція memo знову реєструється як підписник сигналу.
- Значення, збережене в Memo, оновлюється до 4.
- Ми викликаємо всіх підписників Memo.
Потім, викликавши підписників Memo, ми запускаємо Effect (Ефект) і виводимо effect: 4
.
Усі підписники повертаються на свої місця для наступної зміни сигналу :).
По суті, Memo дозволяє нам створювати граф реактивності будь-якого розміру, з покращенням продуктивності завдяки мемоізації важких обчислень.
Не забувайте використовувати createMemo
коли хочете уникнути повторного обчислення важких обчислень.
Підсумок
Ми вивчили, як працюють Signals (Сигнали) за лаштунками з їх автоматичним механізмом підписки.
Далі ми вивчили, як працюють Effects (Ефекти) і як вони підписуються на Signals (Сигнали) за допомогою контексту відслідковування.
Потім ми дізналися, як працює createMemo
і як його можна використовувати, щоб уникнути повторного обчислення важких обчислень.
Signals (Сигнали) — це потужний і популярний інструмент у світі фронтенду.
Тепер, коли ви розумієте, як вони працюють, ви можете створити щось неймовірне за їх допомогою.
Посилання
[
Вивчайте реактивне програмування з SolidJS | Курс від творця SolidJS, Раяна Карніато
SolidJS використовує свою реактивність для створення детальних підписок, які оновлюють лише частини DOM при змінах, замість...
frontendmasters.com
](https://frontendmasters.com/courses/reactivity-solidjs/?source=post_page-----19cbcb6b802b--------------------------------)
[
Документація Solid
Solid — це сучасний JavaScript фреймворк, створений для побудови чуйних і високопродуктивних інтерфейсів користувача (UI). Він…
docs.solidjs.com
](https://docs.solidjs.com/?source=post_page-----19cbcb6b802b--------------------------------)
Перекладено з: Signals behind the scenes