[perf][bundle] Стартовая загрузка ~3.5 МБ JS: нет route-level code splitting; KaTeX, 45 грамматик lowlight, drawio, posthog и AI SDK грузятся всегда #342
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Суть
На слабых машинах интерфейс «задумчивый» уже на старте: при каждой загрузке/перезагрузке страницы браузер скачивает и, главное, парсит/компилирует ~3.5 МБ минифицированного JS (~1 МБ gzip) на главном потоке. Замер реальной прод-сборки (
pnpm --filter client build, rolldown):index(app-код + posthog + highlight + diff + drawio-обвязка + i18next…)lib(ProseMirror/TipTap + lowlight)vendor-mantinekatexДля сравнения: excalidraw (754 КБ), mermaid (~1 МБ суммой диаграммных чанков), emoji-data, onnxruntime/vad — уже корректно отделены и грузятся по требованию. Проблема — в том, что осталось в eager-графе.
Диагноз (по убыванию вклада)
Нет route-level code splitting. App.tsx статически импортирует все 28 роутов, ни одного
React.lazy. Цепочка page.tsx#L3 →FullEditor→ page-editor.tsx#L26-L29 → extensions.ts втягивает весь TipTap,@hocuspocus/provider,yjs,y-indexeddb, все node views и пп. 2–4 ниже в стартовый граф. Пользователь на/loginскачивает и компилирует весь редактор и все страницы настроек.KaTeX статически в eager-чанке редактора. math-inline.tsx#L1-L2 и math-block.tsx#L1-L2:
import katex+ css. Тянутся через extensions.ts#L84-L85. ~280 КБ JS+CSS платят все страницы, включая не содержащие формул.~45 грамматик highlight.js регистрируются на верхнем уровне модуля. extensions.ts#L105-L140:
createLowlight(common)(~35 языков) + 10 статических импортов (powershell, abap, elixir, erlang, dockerfile, clojure, fortran, haskell, scala, plaintext) иlowlight.register(...)выполняются в момент evaluate модуля — для любой страницы, даже без код-блоков. (Стоимость ре-подсветки при печати — #343.)react-drawioбез lazy-обёртки — асимметрия с excalidraw. drawio-view.tsx#L14-L20 и drawio-menu.tsx#L37 импортируются статически через extensions.ts#L93 и page-editor.tsx#L62. У excalidraw при этом есть корректный образец: excalidraw-view-lazy.tsx.posthog-js(~150 КБ) статически даже для self-hosted. main.tsx#L17, main.tsx#L24:posthog.initпод флагомisCloud() && isPostHogEnabled, но импорты статические, иPostHogProviderоборачивает дерево всегда.AI SDK (
ai+@ai-sdk/react) — eager для всех авторизованных. global-app-shell.tsx#L18 статически монтируетAiChatWindow→ chat-thread.tsx#L2 тянет рантайм AI SDK даже при выключенном AI в воркспейсе.Мелочь, уедет само при распиле:
diffв eager-чанке через suggestion.ts#L1;socket.io-client(~90 КБ, реально нужен) — user-provider.tsx#L7.Проверено и уже правильно (не трогать): mermaid (code-block-view.tsx#L11-L13), excalidraw (lazy-обёртки), emoji-mart (+data — динамические import()), onnxruntime/vad (import() при старте диктовки, ассеты из
public/vad/), локали i18n (i18next-http-backend,load: 'currentOnly').Границы изменения
Только
apps/client: App.tsx, lazy-обёртки компонентов, vite.config.ts (группы чанков). Никаких изменений API, поведения фич, серверной части. Функциональность 1:1, меняется только момент загрузки кода.Решение
React.lazy+Suspenseна уровне роутов в App.tsx — минимум:Page(редактор), всеsettings/*, share-страницы,SpaceHome/SpaceTrash/Home. Fallback — существующий скелетон/пустой лоадер. Это само по себе выносит редактор, katex, lowlight, drawio и diff из стартового пути для всех не-редакторских маршрутов.React.lazy-обёртки дляMathInlineView/MathBlockView, katex-чанк грузится при первом появлении math-узла в документе.createLowlightиз top-level: либо инициализировать при первом рендере код-блока, либо сократить eager-набор до короткого списка популярных языков и дорегистрировать остальные динамическим import() по факту встречи языка.drawio-view-lazy.tsx/drawio-menu-lazy.tsxзеркально excalidraw; заменить ссылки в extensions.ts и page-editor.tsx.if (isCloud() && isPostHogEnabled) { const { default: posthog } = await import("posthog-js"); … };PostHogProviderмонтировать только в cloud-режиме (или заменить на no-op обёртку).React.lazyза флагом «AI включён» или по первому открытию окна.advancedChunksдля@tiptap|prosemirror|yjsиkatex, чтобы кэш браузера переживал деплои app-кода.Крайние случаи
Suspenseс плейсхолдером размера узла (как у mermaid)./share/*) используют readonly-редактор — проверить, что распил не ломает их отдельный layout и что нужные extension-чанки догружаются.PageEditorгрузится лениво → провайдер hocuspocus создаётся позже на десятки мс; порядок «сначала статический рендер, потом live-подключение» уже реализован (showStatic), регрессии быть не должно — проверить явно.modulepreload/префетч редакторного чанка после idle на авторизованных роутах.@tabler/icons-react(158 файлов) в prod тришейкаются нормально, но замедляют dev-сервер — можно добавить вoptimizeDeps, отдельная мелкая задача.Тесты / проверка
pnpm --filter client buildдо/после: размер entry-чанка и суммарный eager-JS (цель: entry < ~700 КБ, eager суммарно < ~1.5 МБ min).dist/index.html: в modulepreload не должно быть katex.Вне скоупа
План работ
React.lazyроуты в App.tsx + Suspense-fallback'и, проверка share-layout.