Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Архитектурные принципы Light

Этот документ фиксирует принципы, которым должны соответствовать изменения фреймворка. Принципы — не рекомендации, а требования: если изменение им противоречит, это повод пересмотреть изменение, а не принцип.

Принцип 1: только используемый код попадает в бинарник

Light не должен «стоить» ресурсов пользователю, если он не пользуется функциональностью. Каждая подсистема фреймворка — layout элементов, paint, AccessKit-семантика, обработка событий, оконные возможности — обязана быть опциональной на уровне Cargo-фич. Отсутствие фичи должно гарантированно вырезать соответствующий код из бинарника (через #[cfg]), а не «оставить, но не вызывать».

Правила для разработчиков Light

  1. Новый элемент HTML-разметки (<foo>) добавляется вместе с новой фичей elements-foo. Без фичи разбор <foo> даёт ошибку компиляции с подсказкой включить фичу.
  2. Новая подсистема (валидация, анимации, i18n, devtools, modal-окна, второй главный экран, скриптинг и т. п.) добавляется вместе со своей фичей. Код подсистемы гейтится через #[cfg(feature = "...")].
  3. Кросс-элементные возможности (курсор, z-порядок, align-items, динамические атрибуты) получают свои фичи, если их код сколько-нибудь существенный.
  4. Default-набор фич должен быть минимальным, но практичным: базовые элементы + окно. Всё остальное — opt-in.
  5. Каждая фича должна иметь rustdoc-документацию в Cargo.toml (секция [features] с комментариями) и раздел в guide.md.
  6. При добавлении фичи обязательно обновить:
    • crates/light/Cargo.toml (секция [features]);
    • docs/guide.md (раздел «Установка и Cargo-фичи»);
    • CHANGELOG.md.
  7. Проверка на регрессию: cargo build -p light --no-default-features обязан собираться. cargo build -p light --no-default-features --features <X> обязан собираться для каждой фичи X в отдельности (кроме фич, явно требующих зависимостей).
  8. Размер бинарника — метрика, которую мы измеряем. Раз в N релизов прогоняем cargo bloat и сравниваем с предыдущим замером. Рост без обоснования — повод для ревью.

Компромисс по Style

Поля light::vdom::Style остаются объявленными всегда (несколько байт на элемент). Гейтится не структура данных, а логика, которая эти поля использует, и генерация разбора соответствующих атрибутов макросом. Это позволяет сохранить PartialEq, Default и предсказуемость Style без условной компиляции на уровне полей.

Как это устроено технически

  • Фичи light объявляются в crates/light/Cargo.toml и форвардятся в light-fw-macros (elements-select = [..., "light-fw-macros/elements-select"]), чтобы #[template] знал о включённых элементах.
  • Варианты NodeKind, относящиеся к опциональным элементам, гейтятся #[cfg(feature = "...")]. Как следствие, все их match-ветки компилятор требует закрыть #[cfg] — мёртвый код исчезает сам.
  • Сообщение об ошибке при использовании выключенного элемента называет недостающую фичу и подсказывает строку для Cargo.toml.

Почему это важно

  • Нативные приложения на Rust ценятся за малый footprint. Раздутый фреймворк обесценивает главный аргумент в пользу Rust-стека.
  • Меньше кода — меньше поверхность атаки, меньше шансов на баги в неиспользуемых путях.
  • Compile-time тоже сокращается: меньший крейт компилируется быстрее.

Принцип 2: фичи не должны ломать --all-features

Любое сочетание фич обязано собираться. --all-features — это эталон корректности: фича, которая компилируется только в одиночку, не считается готовой. Обратное тоже верно: базовый набор (--no-default-features плюс минимальные фичи) должен оставаться рабочим.

Потенциальные будущие улучшения

  • light-features-suggest: утилита, которая сканирует #[template(...)] и манифест проекта, находит используемые элементы и атрибуты и предлагает минимальный набор фич. Не реализована: требует рекурсивного разбора шаблонов и манифестов, не критично для основной задачи.
  • docs/footprint-report.md: замеры cargo bloat — --crates (сравнение с предыдущим релизом).
  • Разделение на подкрейты (light-fw-core, light-fw-element-form, light-fw-behavior-validation, …) — начато: ядро вынесено в light-fw-core, компоненты формы и валидации — в отдельные крейты, light стал тонкой мета-обёрткой (см. docs/COMPONENT_MODEL.md).

Пример: ошибка при использовании гейтнутого элемента

Гейтинг работает на этапе компиляции: если шаблон использует элемент, принадлежащий выключенной фиче, #[template] не «молча» превращает его в неизвестный узел, а возвращает понятную ошибку с именем фичи и готовой подсказкой.

Шаблон templates/form.html:

<select v-model="country"></select>

src/main.rs без фичи elements-select (light = { default-features = false, features = ["elements-basic"] }):

#![allow(unused)]
fn main() {
use light::prelude::*;

#[template("templates/form.html")]
struct Form;
}

Компилятор сообщает:

error: элемент <select> недоступен: Cargo-фича `elements-select` выключена у крейта `light`
help: включите её в зависимости, например `light = { features = ["elements-select"] }`

Элемент нужно подключать явно:

[dependencies]
light = { version = "0.1", features = ["elements-basic", "elements-select"] }

Аналогично для <checkbox> (elements-checkbox), <radio> (elements-radio) и <password> (elements-input). Тег <text>/<textarea> входит в elements-input и гейтится так же.