Skip to content

SEO và prerender

SEO là kênh thu hút chính (PRD §12), nên trang không thể chỉ là SPA rỗng.

Chiến lược

Ứng dụng vẫn là SPA Vite, nhưng mỗi URL quan trọng được prerender thành HTML tĩnh lúc build. Cloudflare Pages phục vụ file tĩnh trước; URL nào không có file sẽ rơi về SPA shell qua public/_redirects.

vite build              → dist/           (bundle trình duyệt + index.html khung)
vite build --ssr        → dist-ssr/       (entry-server.js)
node scripts/prerender  → dist/**/index.html + sitemap.xml

Quy trình prerender

  1. Gọi API lấy danh sách trường, đề thi, kỳ thi, giáo viên (phân trang cho đến hết).
  2. Dựng danh sách route: 8 trang tĩnh + một trang cho mỗi bản ghi.
  3. Với mỗi route, prefetch đúng những query mà trang cần, rồi renderToString.
  4. Chèn HTML, thẻ <head> và state của react-query vào khung index.html.
  5. Ghi ra dist/<đường-dẫn>/index.html.
  6. Sinh sitemap.xml (bỏ /tim-kiem/so-sanh vì phụ thuộc tham số).

Nếu API không phản hồi, script vẫn tạo trang (thẻ meta đúng, nội dung do client nạp) và in cảnh báo — bản build không bị hỏng.

Quản lý thẻ head

<Seo> hoạt động theo hai chế độ tuỳ ngữ cảnh:

  • Lúc prerender: ghi vào HeadContext để entry-server đọc lại sau khi render, rồi renderHead() dựng chuỗi HTML.
  • Trên trình duyệt: useEffect ghi thẳng vào document.head khi người dùng điều hướng nội bộ.

Mỗi trang khai báo title, description, canonical, tuỳ chọn noindexjsonLd.

Dữ liệu có cấu trúc

TrangKiểu schema.org
Trang chủWebSite kèm SearchAction
Hồ sơ trườngSchool (có alternateName, address, foundingDate)
Chi tiết kỳ thiEvent (có startDate, organizer, eventAttendanceMode)

Hydrate không gọi lại API

State của react-query được dehydrate vào window.__RQ_STATE__. Khi trang mở, HydrationBoundary nạp lại và các query đã có dữ liệu tươi (staleTime 60 giây) nên không phát sinh request nào — số liệu hiển thị ngay lập tức, kể cả khi mạng chậm.

Giới hạn hiện tại

  • Biến PRERENDER_MAX (mặc định 400) chặn số trang chi tiết mỗi loại. Khi dữ liệu thật vượt mức này, cần nâng giá trị hoặc chuyển sang render theo yêu cầu.
  • Các trang danh sách chỉ prerender trang 1 không kèm bộ lọc; các tổ hợp bộ lọc do client dựng và không nằm trong sitemap.
  • /tim-kiem gắn noindex — kết quả tìm kiếm không nên vào chỉ mục.

Tài liệu nội bộ dự án Chuyên Chọn.