perf: corrigir vazamento do chunk do Recharts + compressão/cache ausentes #21

Merged
jhony75 merged 1 commit from perf/frontend-load-time into develop 2026-09-04 15:14:29 -03:00
Owner

Resumo

Investigando "as páginas estão demorando pra carregar": o chunk do Recharts (~565 KB) não ficava isolado só na rota de Métricas como a documentação afirmava. manualChunks: { charts: ['recharts'] } (atalho por nome de pacote) deixava o Rollup livre pra decidir onde colocar react/react-dom (usados em toda página) — e ele colocava os dois dentro do mesmo chunk do Recharts, criando um import estático do bundle principal pro chunk gigante. Toda página baixava esse chunk, não só Métricas.

Fix: manualChunks(id) explícito em vez do atalho — isola react/react-dom em vendor-react, separado de charts — mais modulePreload.resolveDependencies (o Vite ainda injetava <link rel="modulepreload"> pro chunk de charts no index.html por padrão, mesmo sem o import estático).

Validação

Com vite build + vite preview em porta/cache limpos (Docker não disponível neste sandbox, ver nota abaixo) e Resource Timing API:

  • Antes: página inicial baixava ~223 KB (incluindo os 160 KB gzip do Recharts)
  • Depois: página inicial baixa ~64 KB; só /metricas baixa os 113 KB (gzip) de charts

Outras 3 causas reais encontradas no caminho

  • Fonte bloqueante: Google Fonts saía de @import dentro do CSS (bloqueia carregamento em série — só começa a baixar a fonte depois do CSS principal terminar) pra <link rel="preconnect"> + <link rel="stylesheet"> no index.html.
  • Sem cache de assets: nginx do frontend não tinha Cache-Control — assets com hash do Vite agora são immutable, max-age=31536000; index.html fica no-cache (nunca serve versão velha apontando pra assets que um deploy novo removeu).
  • Sem compressão: nem Traefik nem nginx comprimiam respostas. Adicionado middleware compress no Traefik (compose.yaml), cobrindo API + frontend + docs de uma vez, mesmo padrão de label já usado pro Basic Auth.

Test plan

  • pytest no backend: 37/37 passando (nenhuma mudança de backend)
  • vitest no frontend: 8/8 passando
  • vite build limpo, sem warning de chunk >500 KB
  • Validado com vite preview + Resource Timing API que a página inicial não baixa mais charts-*.js e que /metricas continua funcionando (renderiza os gráficos, sem erros no console)
  • Não testei o container Docker/nginx real (daemon não acessível neste sandbox) — a sintaxe do nginx.conf e dos labels do Traefik foi revisada com cuidado (mesmo padrão de location/add_header e do middleware basicauth já existentes), mas vale um docker compose up local antes do deploy pra conferir os headers de verdade com curl -I.
## Resumo Investigando "as páginas estão demorando pra carregar": o chunk do Recharts (~565 KB) **não** ficava isolado só na rota de Métricas como a documentação afirmava. `manualChunks: { charts: ['recharts'] }` (atalho por nome de pacote) deixava o Rollup livre pra decidir onde colocar `react`/`react-dom` (usados em toda página) — e ele colocava os dois dentro do mesmo chunk do Recharts, criando um **import estático do bundle principal pro chunk gigante**. Toda página baixava esse chunk, não só Métricas. **Fix**: `manualChunks(id)` explícito em vez do atalho — isola `react`/`react-dom` em `vendor-react`, separado de `charts` — mais `modulePreload.resolveDependencies` (o Vite ainda injetava `<link rel="modulepreload">` pro chunk de charts no `index.html` por padrão, mesmo sem o import estático). ### Validação Com `vite build` + `vite preview` em porta/cache limpos (Docker não disponível neste sandbox, ver nota abaixo) e Resource Timing API: - **Antes**: página inicial baixava ~223 KB (incluindo os 160 KB gzip do Recharts) - **Depois**: página inicial baixa ~64 KB; só `/metricas` baixa os 113 KB (gzip) de `charts` ## Outras 3 causas reais encontradas no caminho - **Fonte bloqueante**: Google Fonts saía de `@import` dentro do CSS (bloqueia carregamento em série — só começa a baixar a fonte depois do CSS principal terminar) pra `<link rel="preconnect">` + `<link rel="stylesheet">` no `index.html`. - **Sem cache de assets**: nginx do frontend não tinha `Cache-Control` — assets com hash do Vite agora são `immutable, max-age=31536000`; `index.html` fica `no-cache` (nunca serve versão velha apontando pra assets que um deploy novo removeu). - **Sem compressão**: nem Traefik nem nginx comprimiam respostas. Adicionado middleware `compress` no Traefik (`compose.yaml`), cobrindo API + frontend + docs de uma vez, mesmo padrão de label já usado pro Basic Auth. ## Test plan - [x] `pytest` no backend: 37/37 passando (nenhuma mudança de backend) - [x] `vitest` no frontend: 8/8 passando - [x] `vite build` limpo, sem warning de chunk >500 KB - [x] Validado com `vite preview` + Resource Timing API que a página inicial não baixa mais `charts-*.js` e que `/metricas` continua funcionando (renderiza os gráficos, sem erros no console) - [ ] **Não testei o container Docker/nginx real** (daemon não acessível neste sandbox) — a sintaxe do `nginx.conf` e dos labels do Traefik foi revisada com cuidado (mesmo padrão de `location`/`add_header` e do middleware `basicauth` já existentes), mas vale um `docker compose up` local antes do deploy pra conferir os headers de verdade com `curl -I`.
perf: chunk do Recharts vazava pra toda página + compressão/cache ausentes
All checks were successful
EEG QA CI / validate-and-build (pull_request) Successful in 3m17s
EEG QA CI / Deploy production (pull_request) Has been skipped
EEG QA CI / Deploy development (pull_request) Has been skipped
a27404b353
Achado real por trás de "as páginas estão demorando": o chunk do Recharts
(~565 KB) não ficava isolado na rota de Métricas como a documentação dizia —
manualChunks: { charts: ['recharts'] } (atalho por nome de pacote) deixava o
Rollup livre pra decidir onde colocar react/react-dom (usados em toda
página), e ele colocava os dois dentro do mesmo chunk do Recharts, criando
um import estático do bundle principal pro chunk gigante. Toda página
baixava esse chunk, não só Métricas.

Corrigido com manualChunks(id) explícito (isola react/react-dom em
vendor-react, separado de charts) + modulePreload.resolveDependencies (o
Vite ainda injetava <link rel="modulepreload"> pro chunk de charts no
index.html por padrão). Validado com vite preview + Resource Timing API em
porta/cache limpos: página inicial cai de ~223 KB pra ~64 KB transferidos, e
só a rota de Métricas baixa os 113 KB (gzip) de charts.

Mais três causas encontradas no caminho, menores mas reais:
- Fonte do Google Fonts saía de @import dentro do CSS (bloqueia carregamento
  em série) pra <link rel="preconnect"> + <link rel="stylesheet"> no
  index.html, descoberto em paralelo desde o parse do HTML.
- nginx do frontend não tinha Cache-Control pros assets com hash do Vite —
  agora immutable/max-age longo pros hasheados, no-cache pro index.html
  (pra nunca servir um index.html velho apontando pra assets que sumiram).
- Nem Traefik nem nginx comprimiam respostas. Adicionado middleware
  compress no Traefik (compose.yaml), cobrindo API + frontend + docs de uma
  vez, no mesmo padrão de label já usado pro Basic Auth.

Testado localmente com Docker indisponível neste sandbox (daemon não
acessível) — validado via `vite build`/`vite preview` real + inspeção do
bundle gerado (grep pela string do chunk, Resource Timing API) em vez de um
teste end-to-end do container; a config do nginx/Traefik foi revisada com
cuidado mas não rodou dentro de um container de fato.

37/37 testes de backend e 8/8 de frontend passando.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jhony75 merged commit 689a6e1c95 into develop 2026-09-04 15:14:29 -03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jhony75/eeg-qa!21
No description provided.