Як MobX відстежує залежності?
Коротко
MobX автоматично відстежує залежності між даними (
observable) і обчисленнями (computed,autorun,observer).Коли ти десь читаєш
observable-значення, MobX "запам'ятовує" це як залежність. При наступній зміні цього значення MobX знає, кого сповістити - лише тих, хто реально від нього залежить.
Основна ідея
MobX будує граф залежностей ("дерево реактивності"):
observable → derivation (computed, autorun, observer)observable - джерело даних derivation - все, що читає ці дані reaction - дія, яка запускається при зміні
Щоразу, коли деривація читає observable, MobX реєструє залежність:
"Ця derivation залежить від цього observable".
Приклад 1 - базова механіка
import { observable, autorun } from 'mobx'
const person = observable({
name: 'Tim',
age: 25,
})
// autorun - реакція (derivation)
autorun(() => {
console.log(`Name: ${person.name}`)
})Що відбувається:
autorunзапускається → виконує свій колбек.- Всередині колбека MobX помічає читання
person.name. - MobX реєструє залежність:
autorun #1 залежить від person.name. - Коли
person.nameзміниться, MobX викличеautorunзнову.
autorun не буде викликаний, якщо зміниться person.age, тому що воно не використовувалося в колбеку.
Приклад 2 - як MobX "шпигує" за читаннями
MobX обгортає кожен observable-геттер у proxy або getter/setter, який виконує приблизно наступне:
get name() {
registerDependency(this, 'name') // MobX "запам'ятовує", що це поле було прочитане
return this._name
}Коли запускається реактивна функція (autorun, computed, observer),
MobX тимчасово вмикає "режим запису залежностей".
Усі звернення до observable в цей момент реєструються.
Приклад 3 - з computed
import { makeAutoObservable } from 'mobx'
class Store {
firstName = 'Tim'
lastName = 'Fattore'
constructor() {
makeAutoObservable(this)
}
get fullName() {
return `${this.firstName} ${this.lastName}`
}
}
const store = new Store()
autorun(() => {
console.log(store.fullName)
})Що робить MobX:
fullNameзалежить відfirstNameіlastName;autorunзалежить відfullName;- Граф залежностей:
firstName → fullName → autorun
lastName → fullName → autorun- Зміна
store.firstNameвикликає перерахунокfullName, і тільки якщо значенняfullNameреально змінилося, перезапускаєтьсяautorun.
Жодних зайвих реакцій, все обчислюється "за потребою".
Приклад 4 - React-компонент (observer)
import { observer } from 'mobx-react-lite'
const Counter = observer(({ store }) => (
<div>
<p>{store.count}</p>
<button onClick={() => store.inc()}>+</button>
</div>
))Коли React рендерить <Counter>, MobX:
- фіксує, які
observableбули прочитані (тут -store.count); - підписує компонент лише на ці поля;
- при їх зміні запускає перерендер лише цього компонента.
Тобто кожен observer стає реакцією, а MobX сам реєструє залежності при читанні.
Як MobX реалізує це всередині
- Observable зберігає список своїх підписників (реакцій).
- Derivation (computed/autorun/observer) зберігає, від яких observable вона залежить.
- При читанні observable MobX викликає
reportObserved(), при записі -reportChanged(). - Коли observable змінюється, MobX сповіщає всі derivation, які були зареєстровані.
- Derivation перераховуються лише якщо дійсно зачеплено значення, від якого вони залежать.
Важлива оптимізація
MobX не просто "все перераховує заново" - він робить fine-grained dependency tracking:
autorunперерахується лише якщо його конкретні залежності змінилися.computedкешуються і перераховуються ліниво (лише якщо потрібно).- React-компоненти
observerперерендерюються лише якщо використані observable змінилися.
Підсумок
| Елемент | Роль |
|---|---|
| observable | дані, за якими MobX стежить |
| computed | похідне значення, яке MobX перераховує при змінах |
| reaction / autorun / observer | те, що запускається, коли щось змінилося |
| dependency graph | внутрішня структура, що їх з'єднує |
| fine-grained tracking | лише реальні залежності викликають реакції |
Формула MobX
"Anything that can be derived from the state, should be derived automatically."
Все, що може бути обчислене зі стану, має обчислюватися автоматично.
А MobX робить це, відстежуючи залежності на рівні операцій читання observable.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.