Cover Image for SolidJS — великий обзор
[JavaScript][SolidJS][Frontend]
25 мая 2025 г.

SolidJS — великий обзор

Почему до сих пор React, а не Solid.JS?

В этой статье мы разберёмся, что такое Solid.js — революция, достойный наследник React или очередной фреймворк, который паразитирует на знакомых API, чтобы забрать у React его зависимую аудиторию.

Solid кричит о своей производительности, чистоте, реактивности. Но стоит копнуть глубже, и ты видишь... React. Да, всё те же хуки. Всё та же структура компонентов. Всё те же привычки.

Solid стоит на плечах гигантов. Прямо в своей документации он отсылается к React и Knockout. Если вы раньше писали на React с функциональными компонентами и хуками — вы чувствуете себя почти как дома.

Solid не изобретает новое. Он оптимизирует старое. Он берёт знакомые паттерны и делает их быстрее, проще, прозрачнее. Без виртуального DOM. Без ререндеров компонентов. Без лишней магии.

И вот тут мы подходим к сути: В чём реальное отличие? Почему Solid вообще появился, если "всё и так работает"?

Многие хейтят reconciliation и виртуальный DOM React.

Ко ко ко, медленно. Ко ко ко.

Давайте вспомним для чего это было в реакте. Он появился ради упрощения работы с реальным DOM. Вот в чём идея:

  • В браузере DOM медленный и хрупкий.
  • Разработчику неудобно напрямую управлять узлами, диффами и атрибутами.
  • Вместо этого, React предлагает: просто напиши, как должен выглядеть UI, если у тебя такие-то данные.
  • А React сам под капотом сравнит старое дерево с новым и аккуратно обновит только нужные части (это и есть reconciliation).

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

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

Условный рендеринг выглядит вот так:

<Show when={loggedIn()} fallback={<p>Not logged in</p>}>
  <p>Welcome back!</p>
</Show>

Или так:

const output = createMemo(() => {
  return condition() ? <A /> : <B />;
});

А вот так уже не будет работать:

const [count, setCount] = createSignal(0);

const even = count() % 2 === 0;

return (
  <>
    <button onClick={() => setCount(count() + 1)}>Inc</button>
    {even ? <p>Even</p> : <p>Odd</p>}
  </>
);

even вычисляется один раз при инициализации, потому что count() вызван вне JSX.

Solid не отслеживает count как зависимость для этого выражения.

Изменение count() не вызывает пересоздание even, потому что even не реактивен.

Если ты хочешь использовать условный рендеринг в теле функции, то Делай:

return <>{count() % 2 === 0 ? <Even /> : <Odd />}</>;

Помню потеря реактивности были и с прокси объектами. Например в mobX в прокси тоже можно потерять реактивность, если ты вытаскиваешь значение из реактивного объекта в нерегулярное время — вне реактивного контекста.

const state = reactive({ count: 0 });

const even = state.count % 2 === 0;

watchEffect(() => {
  console.log(even); // ❌ не сработает, even не реактивен
});

Мы выяснили, что потеря реактивности возможна и с прокси и сигналами.

Так почему не Proxy? Зачем мне сигналы? Что вообще такое Signal? Мне так лень... Просто оставлю здесь снипет кода функции readSignal.

export function readSignal(this: SignalState<any> | Memo<any>) {
  const runningTransition = Transition && Transition.running;
  if (
    (this as Memo<any>).sources &&
    (runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state)
  ) {
    if ((runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state) === STALE)
      updateComputation(this as Memo<any>);
    else {
      const updates = Updates;
      Updates = null;
      runUpdates(() => lookUpstream(this as Memo<any>), false);
      Updates = updates;
    }
  }
  if (Listener) {
    const sSlot = this.observers ? this.observers.length : 0;
    if (!Listener.sources) {
      Listener.sources = [this];
      Listener.sourceSlots = [sSlot];
    } else {
      Listener.sources.push(this);
      Listener.sourceSlots!.push(sSlot);
    }
    if (!this.observers) {
      this.observers = [Listener];
      this.observerSlots = [Listener.sources.length - 1];
    } else {
      this.observers.push(Listener);
      this.observerSlots!.push(Listener.sources.length - 1);
    }
  }
  if (runningTransition && Transition!.sources.has(this)) return this.tValue;
  return this.value;
}

Основная магия в списке обсерверов компонент или эффект становится подписчиком.

Можно ли тут обосраться при использовании этого? Так же как мы увидели выше (в примере с потерей контекста).

Да, можно.

Особенно когда ваша целевая аудитория это армия React разработчиков.

В React, все было сделано максимально для упрощения и снижения порога входа. Вспомните только тот момент как продавали хуки, когда они только появились. И функциональные компоненты. Нам сначала продавали их С мотивацией вам сложно на классовых компонентах? Мы сделали все еще проще.

Эх, возможно конечно все это надуманные примеры.

Из самого очевидного, конечно же это то что если ты используешь solid вместо react, то тебе нужно иметь гораздо больший энтузиазм. Ведь аналога того или иного пакета который есть под реакт, может не существовать. Придется создавать его самому. Нужно ли оно тебе, решай сам. Возможно уже завтра все изменится.

Тут должна быть шутка про PHP.

Читать далее

Присоединяйтесь к нашему сообществу