Реактивность
Реактивность Chorda состоит из двух частей:
- Базовая реактивность - включает в себя конфигурацию скоупа и связь между данными и компонентами по схеме PubSub
- Расширенная реактивность - управление сайд-эффектами с помощью событий, асинхронных функций и точек соединения
Ниже пойдет речь о базовой реактивности
info
Базовая реактивность очень проста и работает по следующей схеме:
Изменение -> Реакция -> Патч
Скоуп
Скоуп это набор переменных, доступных компоненту. Архитектура Chorda исходит из того, что состояние компонента описывается только скоупом
tip
Если сравнивать Chorda с React, то скоуп можно понимать как локальный store
info
В Chorda отсутствует понятие контекста как глобального набора данных, доступного всем компонентам. Под контекстом понимается только родительский скоуп
Описание
Локальные переменные компонента задаются в блоке defaults.
export default () => {
return {
defaults: {
// создаем реактивную переменную скоупа
text: () => observable(''),
// в скоуп можно положить и не реактивное значение
name: () => 'MyComponent',
},
}
}
warning
При совпадении имен переменных в контексте и в defaults, будет использована локальная переменная из defaults
Инъекция
Скоуп компонента "содержит" собственные переменные и переменные контекста. Таким образом с помощью инжектирования мы можем добавить переменные, которые определены вне компонента (IoC)
export default () => {
return {
injections: {
cn: (scope) => scope.data, // достаем переменную data из скоупа
},
}
}
tip
Скоуп может быть нереактивным, когда все его элементы являются константами
Передача
При создании дочернего компонента, ему по умолчанию передается скоуп родительского компонента (контекст)
Так в примере ниже компоненты form, button и icon имеют одинаковые скоупы
export default () => {
return {
templates: {
form: {
templates: {
button: {
templates: {
icon: {
}
}
}
}
}
}
}
}
caution
Скоупы именно одинаковые, т.е. набор переменных в них один и тот же, но сами скоупы разные
Реакции
Реакцией является функция, которая возвращает новый частичный набор опций - патч. Реакция связана с переменной скоупа и задается в блоке reactions
export default () => {
return {
reactions: {
// патч содержит новое значение опции text
data: v => ({text: v})
}
}
}
caution
В Chorda реакции это единственный способ динамически создать патч опций, т.е. единственный способ изменить состояние компонента
Переменные
Chorda предлагает свой набор типов переменных для создания реактивного скоупа
observable
Простая реактивная переменная
const val = observable('')
val.$value = 'Hello' // уведомляются все подписчики
computable
Вычисляемая переменная, задается функцией. У нее есть особенности:
- На все реактивные переменные внутри вычисления автоматически устанавливается подписка
- Внутри функции включен терминальный режим
- Чистая функция
const x = observable(1)
const y = observable(10)
const sum = computable(() => x + y)
// sum.$value == 11
x.$value = 5
// sum.$value == 15
warning
Внутри computable нельзя изменять состояние скоупа. Если вы хотите, чтобы ваше вычисление изменяло другие значения, то можете воспользоваться блоком joints
iterable
Обертка-итератор над реактивной переменной
Добавляет переменной метод $each
const it = iterable([1, 2, 3])
it.$each(itm => {
// реактивный элемент массива
})
Ссылочная целостность
Реактивные переменные являются обертками над значениями. При модификациях или присвоениях ссылочно значение остается тем же самым
const obj = {id: 1, name: 'Alice'}
const a = observable(obj)
const b = observable(obj)
a.id = 5
a.$value == b.$value // true
Прокси
Все переменные используют объект Proxy для доступа к своим свойствам. По умолчанию каждое свойство реактивной переменной тоже является реактивным (кроме терминального режима)
Терминальный режим
В терминальном режиме свойства переменной проверяются на формальное отсутствие вложенных свойств (справедливо, к примеру, для примитивных типов). В этом случае свойство считается терминальным и становится доступно по значению
const val = observable({
a: 5,
b: {
c: 'hello'
}
})
// терминальный режим
val.a // 5
val.b // [Proxy]
val.b.c // "hello"