CHANGELOG.md — Tyaff
Все значимые изменения проекта фиксируются здесь.
2026-06-29 - Исправление примера в SPEC.md
Убран антипаттерн из примера динамического контекста. Раньше в примере компонента Page было:
props(incoming) { this.props = incoming; }
Это противоречило описанию API в разделе 2, где сказано что props() опциональна и по умолчанию работает как p => p. Явное присваивание this.props = incoming внутри props() - это антипаттерн, потому что:
- props() должна возвращать значение, которое пойдёт в this.props
- Если props() не определена, то this.props = incoming происходит автоматически
- Прямое присваивание создаёт ложное впечатление что это необходимо
Теперь пример использует this.props напрямую без явного props().
Это делает документацию более согласованной и показывает правильный подход.
[Unreleased]
В работе
- Виртуализация для больших списков (не входит в core)
- Расширение примеров в DOCS.md
- Тестовое покрытие edge cases
2026-06-30
Оптимизация производительности memo-skip path.
Что изменилось для пользователей
- Ускорение memo-skip path — когда
memo() блокирует re-render компонента, обход его детей стал быстрее на 2-5%
- Это улучшает позиции tyaff в бенчмарках против React в сценариях “Memo skip” и “Memo hit”
Ключевые результаты производительности
Ускорение в сценариях memo:
- 1000 детей, parent memo-skip: ~5% быстрее
- Дерево 3906 узлов, все memo-skip: ~5% быстрее
- 1000 детей с props(), memo-skip: ~2% быстрее
- Сокращение аллокаций памяти при update
Поведение не изменилось:
memo() по-прежнему блокирует только текущий компонент
- Дети проходят свою цепочку
props() → memo() → render()
- Context propagation через memo-компоненты работает как раньше
onUpdated() не вызывается при memo-skip
- Все 134 теста проходят
2026-06-30 (раунд 2) — Оптимизации для сценариев где React выигрывал
Раунд 2 оптимизаций производительности. Победа над React в “Update all 5000 rows”.
Что изменилось для пользователей
- Update 1 of 5000 стал на 42% быстрее — обновление одного элемента из 5000 теперь занимает 3.78ms вместо 6.53ms
- Update all 5000 rows стал на 13% быстрее — массовое обновление всех строк теперь 4.78ms вместо 5.48ms
- Insert middle стал на 12% быстрее — вставка в середину списка из 5000 элементов теперь 4.57ms вместо 5.20ms
- No memo (5000) стал на 14% быстрее — сценарий без memo для 5000 элементов теперь 7.72ms вместо 9.00ms
- Memo skip (5000) стал на 6% быстрее — сценарий с memo-skip для 5000 элементов теперь 4.17ms вместо 4.45ms
🏆 Победа над React
В сценарии “Update all 5000 rows” tyaff теперь 4.78ms vs React 6.80ms — tyaff быстрее на 30%. Раньше в этом сценарии был паритет (6.40ms vs 7.00ms по данным от 2026-06-28).
Ключевые результаты производительности
Ускорение в сценариях где React выигрывал:
- Update 1 of 5000: 6.53ms → 3.78ms (-42%)
- Update all 5000 rows: 5.48ms → 4.78ms (-13%)
- Insert middle (5000): 5.20ms → 4.57ms (-12%)
- No memo (5000): 9.00ms → 7.72ms (-14%)
- Memo skip (5000): 4.45ms → 4.17ms (-6%)
Поведение не изменилось:
- Все 134 теста проходят
- API и контракты остались прежними (согласно SPEC.md)
2026-06-28
День оптимизаций производительности и бенчмарков.
Что изменилось для пользователей
- Появился честный бенчмарк против React (bench.html) — 14 сценариев, можно запустить и посмотреть самому
- Re-render 20K элементов стал на 28% быстрее (60ms вместо 83ms)
- Фича “Move between parents” подтверждена тестами — можно безопасно перемещать компоненты между родителями с сохранением instance и state
Ключевые результаты производительности
Tyaff выигрывает у React:
- Swap first/last (5000): 5.70ms vs 34.60ms → в 6 раз быстрее
- Reverse 5000 с ключами: 6.70ms vs 34.90ms → в 5 раз быстрее
- Clear + remount (5000): 12.70ms vs 37.20ms → в 3 раза быстрее
- Move heavy (x50, 10 детей): 0.80ms vs 1.70ms → в 2 раза быстрее
- Move between parents (x50): 0.30ms vs 0.60ms → в 2 раза быстрее
- Update all 5000 rows: 6.40ms vs 7.00ms → паритет с небольшим перевесом
- Deep tree (100 уровней): 5.20ms vs 5.30ms → паритет
React выигрывает у Tyaff:
- Update 1 из 5000: 1.20ms vs 6.40ms → React в 5 раз быстрее
- Memo skip (5000 детей): 1.20ms vs 3.80ms → React в 3 раза быстрее
- Insert middle (5000): 2.40ms vs 5.60ms → React в 2 раза быстрее
- Memo hit (1 из 1000 changed): 0.30ms vs 0.70ms → React в 2 раза быстрее
- No memo (1000 детей): 0.90ms vs 1.80ms → React в 2 раза быстрее
- Mount 5000 components: 34.70ms vs 42.90ms → React на 20% быстрее
- Mount 5000 rows: 24.30ms vs 26.60ms → паритет с небольшим перевесом
Вывод: Tyaff быстрее в массовых операциях (reverse, swap, move между родителями), React быстрее в точечных обновлениях и memo. Разные профили производительности для разных сценариев.
Проделанная работа
Внедрено 7 оптимизаций:
- Оптимизация
escapeKey() через indexOf (~2-3% прирост)
- Условный WeakSet в
unmountVdom (~5ms прирост)
- Кэширование
dom.tagName в applyProps (~2-3% прирост)
- Инлайнинг text node update в
reconcileChildren (~20-30% прирост)
- Инлайнинг same vnode в
reconcileChildren
- Кэш
_nodes при передаче в reconcileChildren (~15-25% прирост)
Promise.resolve() в бенчмарках для честного измерения времени
Откачено 5 оптимизаций (показывает итеративность разработки):
- Keyed reconciliation — сломала тесты на перемещение между родителями
- Dirty tracking — overhead превысил экономию
- Итеративный collectDOMNodes — оказался медленнее рекурсии
- Props shallow equal в reconcileHTML — не дало эффекта на Insert middle
- Проверка nextSibling в syncDOMChildren — не была bottleneck
Добавлено 2 теста в test-node-03.js:
- “instance сохраняется при перемещении компонента с множеством детей” (Move heavy)
- “lifecycle hooks не вызываются при перемещении между родителями”
Обновлена документация
- DEVNOTES.md — добавлен детальный анализ производительности (раздел “tyaff vs React” с таблицами 14 сценариев, разделы “Сильные стороны tyaff” и “Слабые стороны tyaff”)
- AIDEV.md — добавлены конкретные цифры достижений, фича “Move between parents” теперь подтверждена тестами
2026-06-26
День большой реорганизации документации и ролей.
Added
- DOCS.md — полная пользовательская документация (~500 строк, 15 разделов с примерами)
- AGENTS.md — структура проекта и правила взаимодействия AI-агентов
- Концепция “слепков сознания” для AI-агентов (
AIDEV.md, AISEC.md)
- Роли: AIDEV (разработчик) и AISEC (секретарь/адвокат пользователя)
- Правило информационной гигиены: AISEC не читает DEVNOTES.md для сохранения беспристрастности
Changed
- README.md переписан как витрина проекта (~120 строк вместо 800+)
- Полное описание перенесено в DOCS.md
- Уточнён контракт
update() → Promise<boolean>:
- Возвращает результат патча, не результат render
- Promise разрешается сразу, не дожидаясь батча
update({}) → false (пустой патч)
2026-06-25
День уточнения системы ключей и контрактов.
Added
- Production mode через
setDevMode(isDev) — новая экспортируемая функция
- Раздел “Production оптимизации” в SPEC.md
- Известное ограничение: для списков >10K использовать виртуализацию
- Уточнение: все ключевые методы (
props, init, memo, render) имеют доступ к this
Changed
- Система ключей полностью переписана на двух-алгоритмную модель:
- Алгоритм 1: формирование идентификатора (user key
#key vs automatic #parentKey,index vs path-based ,0,1)
- Алгоритм 2: сохранение элемента при обновлении (по совпадению tag + идентификатора)
- Корневой элемент компонента — уточнена трактовка массивов из
render():
- Одиночный vnode →
,0
- Массив → трактуется как неявный Fragment, каждый элемент
,index
- Fragment →
,0, дети ,0,index
- Убрана неоднозначность в примерах деревьев ключей
Fixed
- Исправлены ошибки в комментариях примеров ключей (
,1 → ,0,1, ,1,0 → ,0,1,0)
- Опечатки
h(Fragment} в примерах
- Противоречие: “Fragment прозрачный” vs примеры где он создаёт уровень
2026-06-24
День контрактов API.
Added
update(patch?) → Promise<boolean> — полный контракт:
update() → true (принудительный)
update({}) → false (пустой патч)
update(patch) с изменениями → true
update(patch) без изменений → false
- Batching: несколько
update() в одном тике объединяются, каждый Promise получает свой результат
refresh() обновляет все компоненты, даже если корень — HTML-тег
- Порядок атрибутов для
<select multiple value={[...]}>: multiple → остальные → value/checked
Changed
- Уточнены lifecycle hooks:
onMounted() — children-first (дети монтируются раньше родителей)
onUpdated() — только при update, НЕ при первом mount
onUnmounted() — до удаления DOM (DOM ещё доступен)
- Reserved methods список расширен: добавлены
_rerender, _scheduleUpdate
- Раздел “Known limitation: render → null → render” с recommended workaround через
&&
Added (Browser Support)
- Зафиксированы минимальные версии браузеров:
- Chrome 86+, Firefox 78+, Safari 14+ (для
replaceChildren())
- ES6 modules — все современные браузеры
2026-06-23
День структурирования SPEC.md.
Added
- SPEC.md создан как единый документ спецификации
- Разделы:
- JSX Runtime & VDOM Structure
- Component Factory
- Dynamic Context Tree (pull-based модель)
- Lifecycle
- Update Engine (с лимитом 50 итераций)
- Refs
- Portals with Deferred Mounting
- Reconciliation & Keys
- Attribute Handling
- SVG Namespace
- Controlled Forms
- Mount (универсальная функция)
- Global Refresh
- Global Store Pattern
- Гайдлайн по терминологии: избегать “пропущен”, “подавлен”, “скип”
- Рекомендуемые формулировки с EN↔RU проверкой
Changed
- Переход с “как делать” на “что хотим” в спецификации
- Убраны implementation details из контрактов
- Уточнено:
memo() блокирует только текущий компонент, дети проходят свою цепочку
До 2026-06-22
Ранняя разработка (без git, без формального changelog).
Core реализовано
- VDOM структура:
{ tag, props, childs }
- Функция
h(type, props, ...children)
- Фабрика компонентов
Component(definition)
- Собственный diff/patch алгоритм
- Batching через microtask (
Promise.resolve().then)
this.update(patch?) с shallow comparison
memo(props) с массивом зависимостей
- Lifecycle:
init, onMounted, onUpdated, onUnmounted
this.refs как функция и объект
- Context дерево (pull-based)
- Порталы с отложенным монтированием (
createPortal)
- Fragment с key для перемещения групп
- Универсальный
mount(input, container)
refresh() для глобального обновления
- Защита от рекурсии (лимит 50 итераций)
- Изоляция ошибок в компонентах
Архитектурные решения
- Прямая мутация state вместо иммутабельности
- Props первым аргументом в методах
- Ключи уникальны в пределах всего render (не только среди братьев)
- Нет отдельной сущности “state” — всё на instance
- Нет двусторонних ссылок между vnode и instance
Легенда
- Added — новая функциональность
- Changed — изменения в существующей функциональности
- Fixed — исправления багов
- Removed — удалённая функциональность (пока не было)
Про ведение записей
Проект развивается быстро, поэтому:
- Даты приблизительные для ранних записей (до 2026-06-23)
- Фиксируются значимые концептуальные изменения, не каждая правка
- Разработка идёт без формальных версий, только даты
Последнее обновление: 2026-06-30