Files
transcriber/docs/research/spa-framework.md
T
av 54ca4c0e50 docs: фреймворком приложения выбран Vue 3 с роутером 5 и сборкой Vite
- заведены записка разведки docs/research/spa-framework.md со сравнением Svelte,
  Vue и React на одном экране и решение ADR-2026-08-11-spa-on-vue
- docs/conventions/web-ui.md переписан под Vue: компоненты, маршруты, состояние,
  обращение к API и показ ошибок
- закрыт вопрос «Приложение» в docs/architecture.md, уточнена задача spa-skeleton
2026-08-11 14:51:55 +03:00

11 KiB
Raw Blame History

Фреймворк приложения: Svelte, Vue и React на одном экране

Отвечает на вопрос разведки spa-framework-choice: какой фреймворк берём под приложение на четыре экрана, которое собирается в статику, вшивается в бинарник через go:embed и ставится на телефон.

Кандидатов назвал владелец: Svelte, Vue и React, все с Vite. Мера тоже названа им — размер собранной статики, простота вшивания и цена шага сборки в гейте, а не популярность. Серверную отрисовку и надстройки над фреймворками — SvelteKit, Nuxt, Next — не рассматривали: конвенция ../conventions/web-ui.md уже требует статику в бинарнике, а все три надстройки по умолчанию ждут процесс Node рядом.

Как снималось

Дата замеров — 2026-08-11. Всё считал в каталоге вне репозитория, который удалён вместе с песочницей. Node 24.18.0, npm 11.16.0, Vite 8.2.1, TypeScript 6.0.3.

  • Каркасыnpm create vite@latest <имя> -- --template svelte-ts|vue-ts|react-ts. Из каждого удалил демонстрационные картинки и компонент-счётчик, чтобы в сборку попал только пробный экран.
  • Пробный экран один и тот же по смыслу: список записей, опрос состояния незавершённых раз в две секунды, полоса ошибки, пустое состояние, разбор даты. 60 строк на Vue, 64 на Svelte, 69 на React; стили — один и тот же файл на 15 правил, и в сборке он у всех троих совпал до байта (883 Б), что и подтверждает одинаковость экрана.
  • Четыре маршрута — тот же экран плюс три заглушки и переходы между ними: столько экранов у цели web-access. Роутеры svelte-spa-router 5.1.1, vue-router 5.2.0 и 4.6.4, react-router 8.3.0.
  • Размерыstat -c%s и gzip -9c | wc -c по файлам dist/. Числа Vite в своём выводе печатает по другому уровню сжатия, поэтому в таблицах ниже стоят мои.
  • Установкаnpm ci --cache <свой пустой каталог>; у каждого каркаса кэш свой, иначе первый прогон скачивает общие пакеты за остальных.
  • Сборкаnpm run build трижды подряд с удалением dist и кэша Vite, в таблице лучшее из трёх. Числа сняты на машине разработчика, не в гейте.
  • Совместимость svelte-spa-router со Svelte 5 взята из его описания в реестре, а не проверена: peerDependencies объявляет svelte: ^5.0.0.

Числа

Мера Svelte 5.56.8 Vue 3.5.41 React 19.2.8
Пробный экран, скрипт 35 598 Б / 13 931 Б gzip 62 493 / 24 372 191 797 / 59 679
Четыре маршрута с роутером 45 053 / 17 314 86 890 / 33 326 228 750 / 72 402
Стили, у всех один файл 883 / 474 883 / 474 883 / 474
Файлов в dist/ 3 плюс значок то же то же
Пакетов в установке, каркас 49 48 27
Пакетов с роутером 51 84 (роутер 5) / 50 (роутер 4) 29
node_modules с роутером 74 МБ 92 МБ 91 МБ
Установка с пустым кэшем 7,4 с 9,1 с 9,4 с
npm ci с тёплым кэшем 0,44 с 0,41 с 0,39 с
Сборка и проверка типов 0,32 с плюс 1,16 с 1,08 с 0,81 с

Пакеты считал так: имена первого уровня в node_modules плюс имена второго уровня внутри областей @…. В package-lock.json записей больше — 74, 72 и 69 у каркасов, — потому что он перечисляет двоичные сборки Rollup и oxlint под все платформы, а ставится одна.

Проверка типов у Vue и React входит в npm run build (vue-tsc -b && vite build и tsc -b && vite build), у Svelte вынесена в отдельную команду npm run check и в сборке не участвует — отсюда две цифры в последней строке.

Что оказалось одинаковым и потому ничего не выбирает

  • Вшивание в бинарник. У всех троих dist/ — это index.html, один файл скрипта и один файл стилей с хешем в имени плюс значок. Ни один не кладёт файлов, начинающихся с точки или подчёркивания, поэтому go:embed берёт каталог обычной строкой, без all:.
  • Node в гейте и в образе. Шаг сборки статики нужен всем троим одинаково: на машине разработчика, в task gate и слоем сборки в Dockerfile.
  • Установка на телефон. vite-plugin-pwa 1.3.0 от фреймворка не зависит: в его peerDependencies стоит Vite, и ни одного фреймворка там нет.
  • Цена шага сборки. Секунда с небольшим у всех троих, и на фоне сборки Go и golangci-lint в гейте это не различие.

Различает единственное — размер того, что скачивает телефон, и он расходится вчетверо.

Кандидаты

Svelte

Компилятор, а не библиотека времени выполнения: в собранный файл попадает почти только свой код, отсюда 17 314 Б на четыре экрана — вчетверо меньше React. Реактивность и хранилище состояния встроены, третьей библиотеки под них не нужно.

Против: своего роутера у Svelte нет, а svelte-spa-router держит один человек. Проверка типов идёт отдельной командой, то есть в гейте это второй шаг.

Vue

Библиотека с официальным роутером и официальным хранилищем состояния. 33 326 Б на четыре экрана — вдвое легче React и вдвое тяжелее Svelte. Разметка отделена от кода однофайловым компонентом, документация переведена на русский.

Пятая версия роутера тянет в установку 34 пакета сверх четвёртой (84 против 50): в неё встроена сборочная надстройка под маршруты по файлам. На собранный файл это не влияет — 33 326 Б против 33 848 Б у четвёртой версии, то есть пятая даже чуть легче, — и надстройка не обязательна: замер шёл на своей таблице маршрутов через createRouter, ни один плагин Vite для этого не регистрировался.

Пятая версия — стабильная, а не предварительная: 5.0.0 вышла 29 января 2026, текущая 5.2.0 — 15 июля, метка latest стоит на ней.

React

Экосистема больше, чем у двух других, — числом я её не мерил, — а пакетов в установке меньше всех: 27. Всё остальное против: 72 402 Б на четыре экрана, и ниже этого пола он не опускается, потому что пол задаёт сама библиотека. Роутер, хранилище состояния и работа с запросами — третьими библиотеками, каждая со своим сроком жизни.

Что решено и от чего отказались

Решение — Vue с роутером пятой версии, записано в ADR-2026-08-11-spa-on-vue.

Отвергнуты:

  • Svelte — легче Vue вдвое, но своего роутера не имеет, а тот, что есть, держит один человек. Владелец выбрал экосистему, которая переживёт проект, а не минимальный размер: 33 КБ на телефоне не отличаются от 17 КБ на глаз, а брошенная зависимость отличается;
  • React — вчетверо тяжелее Svelte и вдвое тяжелее Vue, а взамен даёт экосистему, которой приложению на четыре экрана не на что потратиться: чужих компонентов оно не берёт, весь показ данных — список, форма загрузки и текст.

Чего разведка не узнала

  • Как числа изменятся на настоящих экранах. Мерил один экран и три заглушки; загрузка файла с полосой хода, форма настроек и таблица расхода вырастут у всех трёх. Переносится отношение, а не абсолютные значения.
  • Цену готовых наборов компонентов. Не мерил вовсе, а именно она способна удвоить собранный файл.
  • Во что обходится шаг сборки в образе. Слой Node в Dockerfile не собирался: время сборки образа и его вес после добавления слоя неизвестны. Замер сделает spa-skeleton, которая этот слой и пишет.
  • Сколько живёт сборочная надстройка роутера пятой версии. 34 пакета в установке — число, а не суждение о том, как часто они ломаются.