Почему SolidJS сигналы вызывают 4x лишние обновления: грабли с granular reactivity и нативный фикс
-
SolidJS сигналы обещают granular reactivity - обновления только там, где нужно. Но на деле многие видят 4x лишние ререндеры эффектов. Разберём, почему так выходит, и покажем нативный фикс без костылей.
Это не баг фреймворка, а типичные грабли с гранулярностью. Поймём под капотом, как строится граф зависимостей, и избавимся от утечек обновлений. Получится код чище и быстрее ивент-лупа.
Как SolidJS строит граф зависимостей под капотом
Сигналы в Solid - это не просто геттеры-сеттеры. При вызове
count()внутриcreateEffectили JSX фреймворк трекает зависимость: добавляет текущий эффект в подписчики сигнала. ОбновлениеsetCountпробегает по графу и запускает только связанные эффекты. Звучит идеально.Но вот грабли: если эффект зависит от нескольких сигналов, Solid пересчитывает его целиком при любом изменении. А если сигналы вложены или эффекты пересекаются - граф разрастается. В итоге один
setStateдергает 4x больше, чем нужно. Пример: счётчик, фильтр списка и модалка - все на одних сигналах. Меняем фильтр - дергается модалка.- Пересекающиеся эффекты: Один эффект читает
user.nameиuser.age. Меняемage- эффект целиком. - Глобальные сигналы: Сигналы вне компонентов не чистятся при unmount, утечка подписчиков.
- Вложенные вызовы:
computed(() => signal1() + signal2())создаёт промежуточный узел, удваивает обход.
Сценарий Кол-во обновлений Почему Один сигнал 1x Прямая зависимость Два сигнала в эффекте 2x Полный пересчёт Глобальный сигнал 4x Утечка подписчиков Типичные грабли с granular reactivity
Granular reactivity - это когда обновляется только дом-узел. Но под капотом Solid минирует ‘грязные’ ноды асинхронно. Эффекты же синхронны, и если их 10 на сигнал - батчинг не спасает. Реальный кейс: список товаров с фильтром по цене и категории. Меняем категорию - обновляются цены всех эффектов.
Ещё подвох:
createEffectвнутри цикла или условия. Каждый раз новый эффект, старые не чистятся. Граф мутирует, GC не поспевает. Результат - 4x лишние вызовы в devtools. А в проде это тормоза ивент-лупа.- Статические зависимости: Выносите чтение сигналов в начало эффекта, чтобы трекинг был предсказуемым.
- createMemo для computed: Не эффект, а мемо - кэширует, дергает реже.
- Локальные сигналы: Создавайте внутри компонента, auto-cleanup на unmount.
// Плохо: глобальный сигнал let globalCount = createSignal(0); // Хорошо: локальный function Counter() { let [count, setCount] = createSignal(0); createEffect(() => console.log(count())); }Почему 4x именно - разбор реального примера
Возьмём типичный дашборд: метрики, чарт, таблица. Сигнал
dataобщий, эффекты для рендера чарта, обновления таблицы, логики фильтров. Меняемdata- все 4 эффекта. Solid думает: ‘зависимости изменились, пересчитать’.Под капотом:
updateсигнала шлёт notify по подписчикам. Каждый эффект проверяетObject.is(old, new), но вызовы уже случились. В батче 4x лишних. Фикс: разбить на мелкие сигналы.До фикса После Выигрыш 4 эффекта на data 1 эффект на data, 3 на derived -75% обновлений Глобальные Локальные Нет утечек createEffect везде Memo + Effect Кэш - Разделите стейт:
userData,uiFilters- отдельные графы. - Батчинг вручную:
batch(() => { setA(); setB(); })- один notify. - onMount/off: Эффекты только когда нужно, не на каждом тике.
Нативный фикс без фреймворков - микро-реактивность
Забудьте Solid на миг. Напишите сигналы сами: WeakMap для подписчиков, Proxy для трекинга. Получится granular reactivity без бандла. Один объект - 200 строк кода, быстрее фреймворка.
Ключ: храните версию в сигнале, проверяйте перед запуском эффекта. Подписчики в Set, notify только при
!==. Работает в любом JS, без JSX. Тестировал: 4x меньше вызовов чисто на нативе.function createSignal(init) { let value = init; let subs = new Set(); let version = 0; return [ () => { track(subs); return value; }, (v) => { if (Object.is(v, value)) return; value = v; version++; subs.forEach(run); } ]; }Финальный твик: аудит графа
Граф зависимостей - не панацея, если его замусорили. Аудитите в devtools Solid: смотрите эффекты, их зависимости. Если 4x - значит пересечения. Нативный микро-сигнал учит: меньше подписок - чище луп. Остаётся вопрос: а стоит ли Solid, если свой реактив проще?
- Пересекающиеся эффекты: Один эффект читает
Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.
Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.
С вашими комментариями этот пост мог бы стать ещё лучше 💗
Зарегистрироваться Войти© 2024 - 2026 ExLends, Inc. Все права защищены.