Signals: детальная реактивность для фреймворков JavaScript

Как стать разработчиком JavaScript Изучение

В этой статье мы рассмотрим, как использовать сигналы в Solid, современной реактивной библиотеке JavaScript для создания пользовательских интерфейсов, которые в основном полагаются на компоненты.

Введение в сигналы

Одной из последних тенденций в веб-разработке является использование сигналов, которые предлагают более реактивный подход к обновлению значений в программе, которые могут быть изменены. Когда значение обновляется, все, что использует это значение, также обновляется. Именно это делает сигналы такими уникальными.

Рост количества сигналов и интереса к ним напоминает весь тот ажиотаж, который встретил версию 16.8 React в 2019 году, когда команда React представила хуки. Цель хуков заключалась в том, чтобы сделать обновления состояния (и, в конечном счете, все обновления) более функциональными и отказаться от использования классов. Хотя сигналы кажутся почти такими же, как крючки, есть некоторые тонкие различия, которые отличают их друг от друга (которые мы рассмотрим ниже).

Где используются сигналы

  • Библиотека Solid всегда использовала сигналы.
  • Angular недавно принял сигналы в качестве основного способа обнаружения изменений в приложении.
  • Preact недавно представил сигналы как инструмент для управления состоянием.
  • У Vue есть Reactivity API, где refs в значительной степени являются сигналами под другим именем.

Что такое Solid?

Solid (также известный как SolidJS) был создан Райаном Карниато в 2016 году и выпущен в 2018 году. По его собственным словам, это «появилось из-за желания продолжать использовать детализированные реактивные шаблоны, которые я полюбил из Knockout. js».

Он не был поклонником направления, в котором в то время двигались такие библиотеки, как React и Vue, и «просто предпочитал контроль и компонуемость, которые приходят с использованием примитивов, меньших по размеру и независимых от компонентов». Его решение состояло в том, чтобы создать Solid, реактивную среду, которая использует сигналы для создания мелкозернистой реактивности — образец для сигналов, который теперь также можно увидеть во многих других средах.

На первый взгляд Solid очень похож на React с хуками и функциональными компонентами. И в некотором смысле это правильно: они оба разделяют одну и ту же философию, когда дело доходит до управления данными, что значительно упрощает изучение Solid, если мы уже знакомы с React.

Но есть несколько ключевых отличий:

  • Solid предварительно скомпилирован аналогично Svelte. Это означает, что прирост производительности заложен в окончательную сборку, и, следовательно, требуется отправка меньшего количества кода.
  • Solid не использует виртуальный DOM, и хотя компоненты — это просто функции, как в React, они вызываются только один раз при первом рендеринге. (В React они вызываются каждый раз при обновлении этого компонента.)

Что такое сигнал?

Сигналы основаны на шаблоне наблюдателя, который является одним из классических шаблонов проектирования Gang of Four. На самом деле Knockout использует что-то очень похожее на сигналы, известные как «наблюдаемые».

Сигналы — самая атомарная часть реактивного приложения. Это наблюдаемые значения, которые имеют начальное значение и предоставляют методы получения и установки, которые можно использовать для просмотра или обновления этого значения соответственно. Однако, чтобы максимально использовать сигналы, нам нужны реакции, то есть эффекты, которые подписываются на сигнал и запускаются в ответ на изменение значения.

Когда значение сигнала изменяется, он эффективно генерирует событие (или «сигнал»), которое затем запускает реакцию (или «эффект»). Часто это вызов для обновления и рендеринга любых компонентов, зависящих от этого значения. Говорят, что эти компоненты подписываются на сигнал. Это означает, что только эти компоненты будут обновляться при изменении значения сигнала.

Ключевой концепцией Solid является то, что все является эффектом, даже рендеринг вида. Каждый сигнал очень тесно связан с определенными компонентами, на которые он воздействует. Это означает, что при изменении значения представление может быть повторно отрисовано очень мелкозернистым способом без дорогостоящего повторного рендеринга всей страницы.

Читайте также:  Учебник по команде Ping в Ubuntu

Пример сигналов

Чтобы создать сигнал в Solid, нам нужно использовать createSignalфункцию и присвоить две переменные ее возвращаемому значению, например:

const [name, setName] = createSignal("Diana Prince");

Эти две переменные представляют метод получения и установки. В приведенном выше примере nameэто геттер и setNameсеттер. Значение, 0которое передается, createSignalпредставляет начальное значение сигнала.

Разработчикам React это, конечно, покажется знакомым. Код для создания чего-то подобного с помощью хуков React можно увидеть ниже:

const [name, setName] = useState("Diana Prince");

В React геттер ( name) ведет себя как переменная, а сеттер ( setName) — это функция.

Но даже несмотря на то, что они очень похожи, главное отличие состоит в том, что nameв React они ведут себя как переменные, а в Solid — как функции.

Наличие nameфункции означает, что при вызове внутри эффекта она автоматически подписывается на сигнал этого эффекта. А это значит, что при изменении значения сигнала эффект будет работать с использованием нового значения.

Вот пример с нашим name()сигналом:

createEffect(() => console.log(`Hello ${name()}`))

Функцию createEffectможно использовать для запуска эффектов, основанных на значении любых сигналов, таких как запись значения в консоль. Он подпишется на любые сигналы, на которые ссылается функция. Если какие-либо значения сигнала изменятся, код эффекта будет запущен снова.

В нашем примере, если мы изменим значение сигнала nameс помощью setNameфункции установки, мы увидим, как запускается код эффекта, и новое имя регистрируется в консоли:

setName("Wonder Woman")

Наличие геттера в виде функции также означает, что всегда возвращается самое актуальное текущее значение, в то время как другие фреймворки часто могут возвращать «устаревшее» значение даже после его обновления. Это также означает, что любые сигналы можно легко привязать к вычисляемым значениям и запомнить:

const nameLength = createMemo(() => name().length)

Это создает сигнал только для чтения, к которому можно получить доступ с помощью nameLength(). Его значение обновляется в ответ на любые изменения значения сигнала name.

Если name()сигнал включен в компонент, компонент автоматически подпишется на этот сигнал и будет перерендерен при изменении его значения:

import { render } from "solid-js/web"
import { createSignal } from "solid-js"

const HelloComponent = () => {
  const [name, setName] = createSignal("Diana Prince");
  return <h1>Hello {name()}</h1>
}

render(() => <HelloComponent />, document.getElementById("app"));

Обновление значения nameиспользуемого сигнала setNameприведет к HelloComponentповторному рендерингу. Кроме того, HelloComponentфункция вызывается только один раз для создания соответствующего HTML-кода. После того, как он был вызван, его никогда не нужно запускать снова, даже если есть какие-либо обновления значения сигнала name. Однако в React функции компонентов вызываются каждый раз, когда значение, которое они содержат, изменяется.

Другое существенное отличие Solid состоит в том, что, несмотря на использование JSX для логики представления, он вообще не использует виртуальный DOM. Вместо этого он заранее компилирует код с помощью современного инструмента сборки Vite. Это означает, что нужно доставлять гораздо меньше JavaScript, и для него не требуется реальная библиотека Solid (во многом так же, как Svelte). Представление построено в HTML. Затем на лету выполняются детальные обновления — с использованием системы литералов шаблонов для идентификации любых изменений, а затем выполняются старые добрые манипуляции с DOM.

Читайте также:  Руководство по компонентам React: класс против функционала

Эти изолированные и детализированные обновления определенных областей DOM сильно отличаются от подхода React к полной перестройке виртуального DOM после любого изменения. Выполнение обновлений непосредственно в DOM снижает накладные расходы на поддержку виртуального DOM и делает его исключительно быстрым. На самом деле у Solid довольно впечатляющая статистика по скорости рендеринга — он уступает только ванильному JavaScript.

Сигналы в Angular

Как упоминалось ранее, Angular недавно принял сигналы для выполнения мелких обновлений. Они работают аналогично сигналам в Solid, но создаются немного другим способом.

Для создания сигнала signalиспользуется функция и в качестве аргумента передается начальное значение:

const name = signal("Diana Prince")

Имя переменной, которой был назначен сигнал ( nameв приведенном выше примере), затем можно использовать в качестве геттера:

console.log(name)
<< Diana Prince

У сигнала также есть setметод, который можно использовать для обновления его значения, например:

name.set("Wonder Woman")
console.log(name)
<< Wonder Woman

Детальный подход к обновлениям в Angular почти идентичен подходу в Solid. Во-первых, в Angular есть update()метод, который работает аналогично set, но извлекает значение, а не заменяет его:

name.update(name => name.toUpperCase())

Единственная разница здесь в том, что значение ( name) принимается в качестве параметра и выполняется над ним инструкция (.toUpperCase()). Это очень полезно, когда окончательное значение, на которое заменяется геттер, неизвестно и поэтому должно быть получено.

Во-вторых, в Angular также есть computed()функция создания мемоизирующего сигнала. Он работает точно так же, как Solid createMemo:

const nameLength = computed(() => name().length)

Как и в случае с Solid, всякий раз, когда обнаруживается, что значение сигнала в функции вычисления изменилось, значение вычисляемого сигнала также будет меняться.

Наконец, в Angular есть effect()функция, которая работает точно так же, как createEffect()в Solid. Побочный эффект выполняется повторно всякий раз, когда обновляется значение, от которого он зависит:

effect(() => console.log(`Hello`, name()))

name.update(name => name.toUpperCase())
// Hello DIANA PRINCE

Другие особенности Solid

На Solid стоит обратить внимание не только на сигналы. Как мы уже отмечали, он невероятно быстр как при создании, так и при обновлении контента. Он также имеет очень похожий на React API, поэтому его будет очень легко подобрать любому, кто раньше использовал React. Однако под капотом Solid работает совсем по-другому и, как правило, более эффективен.

Еще одна приятная особенность Solid заключается в том, что он добавляет в JSX несколько изящных штрихов, таких как поток управления. Это позволяет нам создавать циклы for с помощью компонента, и мы можем содержать ошибки внутри компонентов с помощью .

Кроме того, компонент также удобен для отображения контента вне обычного потока (например, модальных окон). А вложенная реактивность означает, что любые изменения в массиве значений или объекте будут повторно отображать измененные части представления, а не повторно отображать весь список. Solid делает это еще проще с помощью Stores. Solid также поддерживает рендеринг, гидратацию и потоковую передачу на стороне сервера.

Для тех, кто хочет попробовать Solid, на веб-сайте Solid есть отличное вводное руководство, и мы можем поэкспериментировать с кодом на игровой площадке Solid.

Заключение

В этой статье мы представили концепцию сигналов и то, как они используются в Solid и Angular. Мы также рассмотрели, как они помогают Solid выполнять детальные обновления модели DOM без необходимости использования виртуальной модели DOM. Ряд фреймворков в настоящее время принимает сигнальную парадигму, так что это определенно уловка, которую стоит иметь в рукаве!

Оцените статью
bestprogrammer.ru
Добавить комментарий