Chương 2
CƠ SỞ
LÝ THUYẾT
Nền tảng lý thuyết của đề tài: MENN Stack, tại sao Next.js thay thế React thuần, các cơ chế Hybrid Rendering, hạ tầng triển khai Docker/Coolify và bộ chỉ số Core Web Vitals.
MENN Stack là biến thể hiện đại của MERN Stack truyền thống, thay thế React thuần túy bằng Next.js — framework cho phép Hybrid Rendering. Đây là nền tảng kỹ thuật của toàn bộ đề tài.
MongoDB
DatabaseHệ quản trị cơ sở dữ liệu NoSQL dựa trên tài liệu (document-oriented), cung cấp sự linh hoạt trong việc lưu trữ và truy xuất dữ liệu dưới dạng JSON. Schema-less — phù hợp với blog posts có cấu trúc thay đổi.
Tại sao chọn?
Không cần migration khi thêm field mới vào post. Mongoose ODM cung cấp validation, pre-save hooks và virtual fields.
Express.js
Backend APIFramework web tối giản cho Node.js, đóng vai trò xây dựng hệ thống RESTful API để xử lý các logic nghiệp vụ ở phía Backend: CRUD bài viết, xác thực user, upload ảnh lên Spaces CDN.
Tại sao chọn?
Middleware architecture cho phép xếp chồng: cors → auth → multer → controller. Dễ thêm route mới mà không ảnh hưởng existing logic.
Next.js
Rendering EngineFramework React dành cho Production. Đây là thành phần quan trọng nhất — đóng vai trò "Rendering Engine" cho phép thực hiện các chiến lược SSG, SSR, ISR và CSR trên cùng một ứng dụng.
Tại sao chọn?
Không có framework nào khác cho phép một trang dùng SSG và trang kề dùng SSR trong cùng codebase mà không cần config phức tạp.
Node.js
RuntimeMôi trường thực thi JavaScript phía Server — nền tảng chung cho cả Express backend và các tác vụ server-side của Next.js. Non-blocking I/O model phù hợp với ứng dụng nhiều concurrent request.
Tại sao chọn?
Dùng cùng ngôn ngữ (JavaScript) cho cả Frontend và Backend — giảm context switching, tái sử dụng utility functions và type definitions.
Mặc dù Next.js xây dựng trên nền React, nhưng React SPA bộc lộ nhiều hạn chế mà Next.js khắc phục triệt để trong bài toán tối ưu hiệu năng và SEO.
2.2.1 — Hạn chế của React SPA
"Trang trắng" (Blank Page Problem)
635.83KB JS bundle — vượt ngưỡng 200KB của Lighthouse gấp 3 lầnVới React thuần (SPA), trình duyệt nhận một file HTML rỗng và một tệp JavaScript lớn. Người dùng phải chờ JS thực thi xong mới thấy nội dung — tăng chỉ số FCP (First Contentful Paint) lên mức "Poor" theo chuẩn Google.
<!-- HTML React SPA nhận được từ server -->
<!DOCTYPE html>
<html>
<head>
<script src="/assets/index-Bx3fG2kP.js"></script>
</head>
<body>
<div id="root"></div>
<!-- Trống rỗng — không có nội dung nào -->
</body>
</html>Thách thức SEO
SEO score CSR ≈ 40/100 · SSG/ISR/SSR ≈ 100/100 theo LighthouseGooglebot thường không đợi JavaScript render xong. Khi crawl trang SPA, bot chỉ thấy <div id="root"></div> — không có title, description, hay nội dung bài viết. Kết quả: trang không được lập chỉ mục đúng cách, mất thứ hạng tìm kiếm.
// Googlebot thấy gì khi crawl CSR page:
GET /posts/bitexco-quan-1
Response HTML:
<div id="root"></div>
← Trống. Không có title, h1, p nào.
// Vs Next.js SSG/ISR:
<h1>Bitexco Financial Tower</h1>
<p>Tòa nhà biểu tượng của Quận 1...</p>
← Bot đọc được ngay, lập chỉ mục đầy đủ.Bundle Size & Third-party Overhead
~120KB dependency overhead bị loại bỏ hoàn toànReact SPA yêu cầu cài thêm nhiều thư viện bên thứ ba: react-router-dom (routing), tanstack-query (data fetching), axios (HTTP). Mỗi thư viện thêm vào bundle — parse time tăng, TBT tăng.
// React SPA cần cài thêm:
npm install react-router-dom // +50KB
npm install @tanstack/react-query // +40KB
npm install axios // +30KB
// Next.js có sẵn:
// ✓ File-based routing (zero config)
// ✓ getStaticProps / getServerSideProps
// ✓ fetch() native với cache control
// ✓ API Routes (không cần axios)2.2.2 — Ưu thế vượt trội của Next.js
Hybrid Power
Next.js không ép buộc toàn bộ ứng dụng phải theo một cơ chế render duy nhất. Mỗi trang có thể độc lập chọn chiến lược render tối ưu nhất.
| Page | Mode | Lý do |
|---|---|---|
| /ssg/homepage | SSG | Landing page — data ít đổi, cần load cực nhanh |
| /isr/homepage | ISR | Blog list — cập nhật định kỳ, không rebuild toàn site |
| /ssr/homepage | SSR | Search results — data realtime theo query |
| /csr/homepage | CSR | Demo SPA — minh họa hạn chế của CSR |
| /admin/dashboard | CSR | Admin — không cần SEO, nhiều interaction |
Zero Config Optimization
Next.js tích hợp sẵn các tối ưu hóa mà React SPA phải tự cấu hình hoặc cài thêm thư viện.
Image Optimization
Tự động nén, resize và chuyển sang WebP. <Image> component tự set width/height → tránh CLS.
Automatic Code Splitting
Mỗi route có JS chunk riêng. Trang /posts/[slug] không tải JS của /admin.
Link Prefetching
<Link> component tự prefetch trang đích khi người dùng hover → chuyển trang tức thì.
Font Optimization
next/font tự host Google Fonts — loại bỏ external request, tránh FOUT (Flash of Unstyled Text).
API Routes — BFF Pattern
Next.js cho phép viết API trung gian (Backend for Frontend) ngay trong codebase frontend. Trong dự án này, API routes đóng vai trò proxy bảo mật — browser không bao giờ biết địa chỉ Express backend.
// pages/api/blog-proxy.js
// Browser gọi /api/blog-proxy
// Next.js server gọi Express backend (internal)
// Browser không bao giờ thấy INTERNAL_BACKEND_URL
export default async function handler(req, res) {
const BACKEND = process.env.INTERNAL_BACKEND_URL;
const data = await fetch(`${BACKEND}/api/posts`);
res.json(await data.json());
}Cốt lõi của Hybrid Rendering là khả năng kết hợp linh hoạt bốn phương thức sau trên cùng một ứng dụng Next.js:
2.3.1
Static Site Generation (SSG)
Khi nào render: Build timeƯu / Nhược điểm
Phù hợp cho
Trang giới thiệu (About Us), Landing Page quảng cáo, Tài liệu hướng dẫn kỹ thuật (Docs)
getStaticProps()Request Flow
2.3.2
Server-Side Rendering (SSR)
Khi nào render: Mỗi requestƯu / Nhược điểm
Phù hợp cho
Trang kết quả tìm kiếm, Bảng tin mạng xã hội, Trang giá vàng/chứng khoán realtime
getServerSideProps()Request Flow
2.3.3
Client-Side Rendering (CSR)
Khi nào render: Sau khi JS bundle loadƯu / Nhược điểm
Phù hợp cho
Dashboard quản trị, Trang cài đặt tài khoản, Ứng dụng quản lý công việc nằm sau login
useEffect() + fetch()Request Flow
2.3.4
Incremental Static Regeneration (ISR)
Khi nào render: Build time + background revalidateƯu / Nhược điểm
Phù hợp cho
Trang chi tiết sản phẩm thương mại điện tử, Bài viết Blog/Tin tức, Danh mục sản phẩm
getStaticProps() + revalidateRequest Flow
2.4.1
VPS & DigitalOcean
- Máy chủ ảo riêng (VPS) — toàn quyền kiểm soát từ hệ điều hành đến cấu hình mạng
- Droplet SGP1 (Singapore) — latency đến HCM City < 15ms
- Phân bổ tài nguyên tối ưu: toàn bộ stack (Frontend + Backend + DB) trên 1 Droplet $18/tháng
- Không bị giới hạn bởi platform rules của Vercel hay Heroku
2.4.2
Docker & Coolify
- Docker: Đóng gói ứng dụng vào Container — đảm bảo chạy đồng nhất trên mọi môi trường (Local → Staging → Production)
- Coolify: Self-hosted PaaS — tự động hóa cấu hình Traefik, cấp phát SSL (Let's Encrypt), CI/CD từ GitHub
- Nixpacks: Auto-detect build system — không cần viết Dockerfile thủ công
- Zero-downtime deploy: Health check trước khi switch traffic sang container mới
Để đánh giá hiệu quả của Hybrid Rendering, đề tài dựa trên bộ chỉ số Core Web Vitals của Google — thước đo khách quan nhất về trải nghiệm người dùng thực tế.
Largest Contentful Paint
Thời gian hiển thị phần tử nội dung lớn nhất
Tối ưu: < 2.5sFirst Input Delay
Thời gian phản hồi khi người dùng tương tác lần đầu
Tối ưu: < 100msCumulative Layout Shift
Độ ổn định của bố cục trang web
Tối ưu: < 0.1Hybrid Rendering cải thiện Core Web Vitals như thế nào?
SSG/ISR có HTML + image URL sẵn → browser preload ngay. CSR phải chờ JS → fetch → render.
SSG/ISR biết dimensions trước → reserve space. CSR skeleton swap gây layout shift.
CSR bundle lớn hơn (inline toàn bộ components) → main thread bị block lâu hơn.
SSG serve file tĩnh từ disk. SSR phải fetch DB + render. CSR HTML rỗng nhanh nhưng FCP chậm.
Xem chi tiết tại: /docs/vitals · Đo lường thực tế tại: /seminar/experiment
Tiếp theo
Chương 3: Thực nghiệm
Xây dựng ứng dụng thực tế, đo lường và so sánh hiệu năng 4 rendering mode trên môi trường Cloud