Первый взгляд на Gea — UI-фреймворк, построенный вокруг компилятора
Gea — компилятор-ориентированный JavaScript UI framework с JSX, классами store и прямыми DOM-исправлениями. Статья разбирает props, размеры и зрелость.
Gea — это JavaScript-фреймворк для UI с компилятором во главе угла: плагин для Vite читает ваш JSX на этапе сборки, определяет, какие DOM-узлы зависят от какого состояния, и генерирует прямые патчи DOM. Виртуального DOM здесь нет, и во время выполнения ничего не диффится.
Фреймворки, обещающие «скомпилировать рантайм в ноль», появляются каждые пару месяцев, и анонсирующий пост обычно даёт вам питч и цифры, почти ничего не говоря о компромиссах. Страницы Gea читаются примерно так же, поэтому далее мы разберём модель в том виде, как её описывает проект, укажем, откуда берётся каждая цифра, и подробнее всего рассмотрим то, что действительно отличает Gea от React и Vue: двусторонние объектные props.
Ключевые выводы
- Gea связывает реактивность на этапе компиляции: плагин Vite анализирует JSX, сопоставляет состояние с DOM-узлами и генерирует точечные патчи вместо того, чтобы поставлять виртуальный DOM.
- Вся модель сводится к трём правилам: stores — это классы, наследующие
Store, компоненты — это классы с методомtemplate()или обычные функции, а вычисляемые значения — это геттеры. - Объектный или массивный prop передаёт дочернему компоненту тот же самый прокси, который держит родитель, поэтому запись в дочернем компоненте меняет и состояние родителя. Примитивы, наоборот, копируются.
- Проект сообщает о 121 Б в brotli для hello world, 4,9 КБ для интерактивного todo и результате 1.02 в js-framework-benchmark — все замеры сделаны на Gea 1.3.0 самим мейнтейнером на его же сборках.
- С одним мейнтейнером, ранним номером версии и без независимого аудита Gea заслуживает вечернего эксперимента, а не ставки в продакшене.
Что такое Gea?
Gea переносит реактивность из рантайма в сборку. Согласно сайту проекта, его плагин для Vite читает ваш JSX во время компиляции, определяет, какие DOM-узлы зависят от каких фрагментов состояния, и настраивает патчи, затрагивающие только эти узлы. Сравнение с Vue формулирует контраст прямо: там, где Vue заново выполняет render-функцию и диффит получившееся дерево, Gea просто вызывает патч-функции, которые компилятор уже сгенерировал. Ваше состояние живёт в обычных классах, которые Gea оборачивает в глубокий прокси, так что обыкновенного присваивания вроде this.count++ достаточно, чтобы обновить DOM. Нечего оборачивать и нет списка зависимостей, который нужно поддерживать в актуальном состоянии. README представляет эту родословную как продолжение более ранних библиотек мейнтейнера — erste.js и regie — теперь с трансформациями JSX на этапе компиляции.
Что представляет собой модель Gea в трёх строках?
Вся поверхность Gea укладывается в три правила: stores — это классы, наследующие Store, компоненты — это классы с методом template() или обычные функции, а вычисляемые значения — обычные геттеры. Счётчик из README демонстрирует всё сразу:
// counter-store.ts
import { Store } from '@geajs/core'
class CounterStore extends Store {
count = 0
increment() { this.count++ }
decrement() { this.count-- }
}
export default new CounterStore()
// app.tsx
import { Component } from '@geajs/core'
import counterStore from './counter-store'
export default class App extends Component {
template() {
return (
<div>
<h1>{counterStore.count}</h1>
<button click={counterStore.increment}>+</button>
<button click={counterStore.decrement}>-</button>
</div>
)
}
}
Документация по компонентам объясняет, что плагин Vite во время сборки переписывает функциональные компоненты в классовые, и что шаблон компонента выполняется ровно один раз. Всё, что происходит дальше, — это патч, а не повторный рендер.
Чем JSX в Gea отличается от JSX в React?
JSX в Gea выглядит как в React, и одну условность действительно придётся изменить: пишите class там, где React ждёт className. С событиями всё мягче. README записывает их как атрибуты в нижнем регистре — click, input, change, — но сравнение с Vue в документации подтверждает, что onClick, onInput и onChange тоже принимаются, так что обработчики в стиле React переносятся как есть. А ref не принимает ref-объект: Gea кладёт DOM-узел прямо в свойство компонента, как только рендер завершён.
// Привычка из React // Эквивалент в Gea
<div className="card" <div class="card"
onClick={save} /> click={save} />
Паттерн ref в документации — это поле класса, инициализированное как null, атрибут ref={this.videoEl} на элементе, а затем прямое использование this.videoEl после рендера.
Props: объекты — это прокси родителя
Главное отступление от React и Vue — то, что происходит с непримитивным prop. Документация по компонентам Gea объясняет, что дочерний компонент получает тот же самый прокси, которым владеет родитель, поэтому любая запись из дочернего компонента попадает в состояние родителя и отражается в его DOM. Примитивы ведут себя так же, как аргументы функций в JavaScript: дочерний компонент получает копию, и переприсваивание не меняет ничего за его пределами. В документации показан дочерний компонент, делающий ровно это:
export default class Editor extends Component {
rename() {
this.props.user.name = 'Bob' // parent's DOM updates too
}
template({ user }) {
return <button click={this.rename}>{user.name}</button>
}
}
Правило работает на любой глубине дерева. Передайте ту же ссылку внуку, дайте ему записать в объект — и все предки, наблюдающие за этими данными, перерисуются. Ничего не нужно поднимать наверх через колбэк, и между ними нет ни emit, ни v-model.
| Дочерний обновляет родителя | React | Vue | Gea |
|---|---|---|---|
| Объекты/массивы | Props-колбэки | emit / v-model | Прямая мутация общего прокси |
| Примитивы | Props-колбэки | emit / v-model | Невозможно (передача по значению) |
Документация подаёт это исключительно как преимущество, а открытые вопросы остаются без ответа. В дереве из 40 компонентов — какой именно компонент изменил этот объект? Однонаправленная конвенция React существует отчасти для того, чтобы у изменений было отслеживаемое происхождение; Gea меняет это на прямолинейность, и пока никто не описал, во что этот обмен обходится на масштабе. То же касается и самой связки на этапе компиляции: как компилятор обрабатывает код, который он не может проанализировать статически, документация не рассматривает.
Размер и скорость — в том виде, как их сообщает проект
Каждая цифра о производительности Gea — это собственное измерение проекта на собственных сборках конкурирующих фреймворков, сделанных мейнтейнером. Таблицы размеров в README дают приложению hello-world 121 Б JavaScript в brotli — рядом со сборками проекта для Solid (3,6 КБ), Svelte (8,5 КБ), Vue (20,7 КБ) и React (50,8 КБ), при 4,9 КБ для интерактивного todo. Оба набора цифр сняты на Gea 1.3.0 со свежих production-сборок Vite 8.0.10.
Результат 1.02 в js-framework-benchmark, где 1.00 — это написанный вручную ванильный JavaScript, получен в собственном прогоне набора тестов проектом на Chrome 147, а не в официальном опубликованном раунде. Фраза «самый быстрый компилируемый UI-фреймворк» на главной странице — это утверждение проекта, построенное на тех же самостоятельно полученных цифрах, а не вывод третьей стороны.
Что поставляется вместе с Gea?
Таблица пакетов в README перечисляет @geajs/core, @geajs/vite-plugin, @geajs/ssr для серверного рендеринга, create-gea для скаффолдинга, плюс @geajs/ui (headless-компоненты на базе Zag.js) и @geajs/mobile для мобильных примитивов. gea-tools — это расширение для VS Code и Cursor, а не npm-пакет.
Более необычный пункт — инструментарий для AI. Команда npx skills add dashersw/gea устанавливает набор agent skills, которые живут в репозитории по пути .cursor/skills/gea-framework и передают AI-ассистенту для кодинга те соглашения, о которых иначе ему пришлось бы догадываться: как работают stores, как объявляются компоненты и чем отличается JSX. Для молодого фреймворка, по которому ни у одной модели нет обучающих данных, поставка соглашений в виде потребляемых редактором skills — прагматичный ход для онбординга.
Зрелость: о чём говорят номера версий
Сравнительные таблицы и таблицы размеров в README сняты на Gea 1.3.0, а @geajs/core с тех пор опубликовал 1.4.0, согласно release notes проекта. Gea распространяется под лицензией MIT, и его поддерживает один человек — Armagan Amcalar. Все цифры по бенчмаркам и размерам являются самоотчётными, и независимого технического обзора фреймворка пока не существует. Для команды вывод прост: модель непротиворечива и достаточно компактна, чтобы оценить её за один вечер, но единственный мейнтейнер, ранний номер версии и непроверенные цифры означают, что пока нет доказательной базы, чтобы ставить на неё продукт.
По-настоящему интересное утверждение Gea — не размер бандла; оно в том, что обычные классы, функции и геттеры способны нести полноценную модель реактивности, если связывание берёт на себя компилятор. Самый быстрый способ оценить это утверждение — собрать счётчик и приведённый выше пример с props и посмотреть, ощущаются ли двусторонние объектные семантики как ясность или как источник проблем при отладке именно в вашем типе кодовой базы.
Частые вопросы
Чем Gea отличается от Solid и Svelte?
Все три опираются на компилятор, но реактивный примитив у них разный. Solid строит реактивность на сигналах, которые выполняются в браузере, а Svelte 5 использует синтаксис рун, скомпилированный из собственного языка шаблонов. У Gea нет ни того, ни другого: состояние хранится в обычных классах, обёрнутых глубоким прокси, а плагин Vite читает стандартный JSX на этапе сборки, чтобы связать прямые патчи DOM.
Какие хуки жизненного цикла есть у компонентов Gea?
Классовые компоненты Gea предоставляют четыре хука. created(props) срабатывает между конструктором и первым рендером — именно туда документация советует помещать логику инициализации. onAfterRender() выполняется, когда элемент компонента уже в документе, а его дочерние элементы смонтированы. onAfterRenderAsync() ждёт следующего requestAnimationFrame. dispose() извлекает компонент из DOM и разрушает его наблюдателей и потомков. Документация направляет вас к классовым компонентам всякий раз, когда что-либо из этого требуется.
Работает ли Gea с TypeScript?
Да. Классовый компонент объявляет форму своих props через 'declare props' — ambient-объявление, которое не порождает JavaScript, но позволяет любому редактору с поддержкой TypeScript дополнять и проверять атрибуты, без участия плагина фреймворка. Аннотация параметра template() как this['props'] переносит эти типы в деструктурированные переменные внутри метода; без неё они откатываются к any. Функциональные компоненты получают то же самое через обычную аннотацию параметра.
Как начать новый проект на Gea?
Создайте каркас командой 'npm create gea@latest' — это инструмент create-gea от проекта. Сборки Gea работают поверх Vite, а @geajs/vite-plugin отвечает за трансформацию JSX, связывание реактивности и горячую перезагрузку. В документации также есть руководство по использованию в браузере для запуска Gea без шага сборки, наряду с руководствами по роутеру, UI-киту и мобильным пакетам.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k