Giao diện
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.xmlQuy trình prerender
- Gọi API lấy danh sách trường, đề thi, kỳ thi, giáo viên (phân trang cho đến hết).
- Dựng danh sách route: 8 trang tĩnh + một trang cho mỗi bản ghi.
- Với mỗi route, prefetch đúng những query mà trang cần, rồi
renderToString. - Chèn HTML, thẻ
<head>và state của react-query vào khungindex.html. - Ghi ra
dist/<đường-dẫn>/index.html. - Sinh
sitemap.xml(bỏ/tim-kiemvà/so-sanhvì 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ồirenderHead()dựng chuỗi HTML. - Trên trình duyệt:
useEffectghi thẳng vàodocument.headkhi người dùng điều hướng nội bộ.
Mỗi trang khai báo title, description, canonical, tuỳ chọn noindex và jsonLd.
Dữ liệu có cấu trúc
| Trang | Kiểu schema.org |
|---|---|
| Trang chủ | WebSite kèm SearchAction |
| Hồ sơ trường | School (có alternateName, address, foundingDate) |
| Chi tiết kỳ thi | Event (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-kiemgắnnoindex— kết quả tìm kiếm không nên vào chỉ mục.