Реактивность: круг за кругом — 20 июля 2026 г. в 06:05:42.024
Реактивность: круг за кругом На этих выходных я наконец поставил точку в первой лекции нового интенсива «Vue под капотом». Главная особенность цикла «Под капотом» в том, что я хочу не просто показать, как устроен фреймворк сейчас, а провести вас по всей его эволюции — от первых идей до современного состояния. С React это было просто: библиотека, как огромный титаник, упрямо идёт своим курсом. А Vue — совсем другой. Он постоянно лавирует, подглядывает удачные решения у соседей, экспериментирует. Я, кстати, считаю, что могу говорить об этом с полным правом — я работал со Vue более десяти лет, начиная ещё с версии 0.10 вплоть до версии 3.5. И вот в чём ирония: после всех этих манёвров Vue совершил красивый виток по спирали и вернулся очень близко к тому, с чего начинал. И знаете, когда погружаешься в историю технологий, постоянно ловишь себя на мысли: почти всё, что нам продают как «революцию», — это хорошо забытое старое в новой обёртке. Блеск новизны ослепляет, и мы легко забываем, что уже видели это раньше. Но хватит лирики. Давайте лучше на конкретных примерах: возьмём базовую задачу — отслеживание изменений состояния и их влияния на интерфейс. Идея за все эти годы почти не изменилась. А вот реализации — ходят по кругу. React выбрал прямолинейный путь: хочешь обновить состояние — явно вызови setState (или useState). Vue пошёл другим маршрутом. Над обычным объектом создавалась «прокси-обёртка» через Object.defineProperty, которая перехватывала все чтения и записи: function reactive(obj) { const observed = {}; for (const key of Object.keys(obj)) { let value = obj[key]; Object.defineProperty(observed, key, { get() { // Запоминаем, кто читает track(observed, key); return value; }, set(newValue) { if (newValue === value) return; value = newValue; // Уведомляем всех trigger(observed, key); } }); } return observed; } Красиво, но с серьёзным ограничением: все свойства нужно было объявить заранее. Нельзя было просто добавить новое поле — магия переставала работать. Приходилось использовать отдельные «костыли». Хотелось больше волшебства. И тогда появился Vue 3 с Proxy: function reactive(obj) { return new Proxy(obj, { get(target, key) { track(target, key); return target[key]; }, set(target, key, value) { target[key] = value; trigger(target, key); return true; } }); } Теперь можно было добавлять и удалять свойства на лету. Делать любые изменения, которые только хочется. Казалось, вот оно — идеальное решение. Но со временем стали проявляться минусы: высокая стоимость использования таких объектов, сложности с отладкой, неожиданные баги «из-за магии». И вот на сцену выходит подход на основе сигналов — решение, которое активно развивают Solid, Preact Signals, а Vue 3.6+ взял лучшее из него в свою систему реактивности. function signal(initialValue) { let value = initialValue; const subscribers = new Set(); const read = () => { if (activeEffect) subscribers.add(activeEffect); return value; }; const write = (newValue) => { if (newValue === value) return; value = newValue; subscribers.forEach(fn => fn()); }; return [read, write]; } Знакомо? Мы вернулись к более явному разделению чтения и записи, но уже на новом технологическом уровне с продвинутым push-pull алгоритмом. Магии стало заметно меньше — и это хорошо. Бенчмарки впечатляют. Proxy по-прежнему используется, но его роль теперь дополнена гораздо более эффективной системой отслеживания и обновлений на основе сигналов.

