Реактивность во Vue.js строится вокруг паттерна Observer
(Наблюдатель). Его задача — автоматически отслеживать
зависимости между состоянием и представлением и запускать обновления при
изменении данных. Реализация этого паттерна во Vue эволюционировала: от
Object.defineProperty во Vue 2 к Proxy во Vue
3, при этом концептуальная модель осталась прежней — есть источник
изменений, есть подписчики, есть механизм оповещения.
В абстрактном виде паттерн состоит из трёх элементов:
Во Vue роль Subject выполняют реактивные данные, а роль
Observer — вычисляемые значения, компоненты и эффекты
рендеринга.
Во Vue 2 каждая реактивная переменная превращается в геттер и сеттер:
Object.defineProperty(obj, 'count', {
get() {
return value
},
set(newValue) {
value = newValue
notify()
}
})
Эта логика инкапсулирована в классе Observer, который
рекурсивно обходит объект и делает каждое поле реактивным.
Внутри Vue 2 паттерн Observer реализован через связку Dep и Watcher.
depend() и
notify().class Dep {
subs = []
depend() {
if (Dep.target) {
this.subs.push(Dep.target)
}
}
notify() {
this.subs.forEach(w => w.update())
}
}
class Watcher {
UPDATE() {
// повторный рендер или вычисление
}
}
Во время рендера компонента текущий Watcher помещается в
Dep.target, что позволяет автоматически собрать
зависимости.
Ключевая особенность реализации — отсутствие явной подписки. Зависимости собираются во время выполнения:
Watcher.get.get регистрирует Watcher в
Dep.notify.Этот механизм обеспечивает минимальное количество обновлений и точечный ререндер.
Использование Object.defineProperty накладывает ряд
ограничений:
Из-за этого появились вспомогательные методы вроде
Vue.se t.
Во Vue 3 реактивность переписана с использованием
Proxy.
const state = new Proxy(target, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key)
return true
}
})
Во Vue 3 роль Dep выполняет система хранилищ
зависимостей:
const targetMap = new WeakMap()
Использование WeakMap позволяет автоматически очищать
неиспользуемые зависимости и предотвращать утечки памяти.
Watcher во Vue 3 заменён на effect:
effect(() => {
console.log(state.count)
})
effect регистрируется как наблюдатель.Компоненты, вычисляемые свойства и watch-функции построены поверх этого механизма.
По умолчанию Vue делает реактивность глубокой:
Паттерн Observer не обновляет DOM напрямую. Он лишь сигнализирует о необходимости:
Таким образом, Observer отвечает только за реакцию на изменение состояния, а не за отображение.
Observer во Vue:
Вся реактивная система Vue — это специализированная, оптимизированная реализация паттерна Observer, адаптированная под особенности JavaScript и компонентной архитектуры.