Chương 3 — Triển khai & Đánh giá

Thực Nghiệm

Toàn bộ hệ thống được triển khai lên môi trường Internet thực tế trước khi đo lường. Không đo trên localhost. Công cụ: Google Lighthouse Desktop.

Mục 3.3.1

Triển khai hệ thống

Hosting

DigitalOcean VPS

Máy chủ ảo độc lập

Tài nguyên xử lý riêng biệt, phản ánh chính xác tốc độ thực tế của hệ thống. Không bị ảnh hưởng bởi các tenant khác.

Automation

Coolify

Tự quản lý trên VPS

Tương tự Vercel/Heroku nhưng tự cài trên VPS. Tự động kéo code mới, đóng gói Docker, và khởi chạy cả 3 phân hệ.

Domain

ie213saigonblog.online

HTTPS + SSL

Hệ thống đã được cấu hình định tuyến thành công và chạy chính thức dưới tên miền thực tế với SSL.

🐳

Coolify tự động hoá việc kéo mã nguồn mới nhất, đóng gói môi trường (Docker) và khởi chạy mượt mà cả 3 phân hệ: Frontend (Next.js), Backend (ExpressJS) Cơ sở dữ liệu (MongoDB) — tất cả trên cùng một VPS duy nhất.

Mục 3.3.2 – 3.3.5

Kết quả đo lường Lighthouse

SSG

Lighthouse Performance: Xuất sắc

SI: ~0.6s · TTFB: ~10ms · FCP: ~0.4s · LCP: ~0.7s

92/100

Filmstrip Timeline (mô phỏng)

0msTrắng
200msNội dung
600msNội dung
1.0sẢnh
1.5s+Hoàn chỉnh

Sau khi gửi yêu cầu, trình duyệt đã nhận trang được build sẵn. Song song đó, hình ảnh là nội dung nặng nhất nên cần thêm thời gian để hiển thị.

TTFB

~10ms

Server response

FCP

~0.4s

First paint

LCP

~0.7s

Largest paint

SI

~0.6s

Speed Index

Nguyên nhân điểm cao

Trang đã được đóng gói thành file HTML tĩnh ngay tại npm run build. Máy chủ Next.js hoàn toàn không phải thực thi lệnh gọi API nào tới Backend khi người dùng truy cập — CDN phục vụ file tức thì.

Kết luận

Trang SSG cung cấp trải nghiệm tốc độ hoàn hảo nhất. Tuy nhiên, dữ liệu trên trang bị cố định — bài viết mới sau thời điểm Build không xuất hiện.

Đánh đổi: Dữ liệu đóng băng tại build time.

ISR

Lighthouse Performance: Xuất sắc

SI: ~0.6s · TTFB: ~10ms · FCP: ~0.4s · LCP: ~0.7s

92/100

Filmstrip Timeline (mô phỏng)

0msTrắng
200msNội dung
600msNội dung
1.0sẢnh
1.5s+Hoàn chỉnh

Sau khi gửi yêu cầu, trình duyệt đã nhận trang được build sẵn từ cache và chỉ cần chờ hình ảnh load vào trình duyệt.

TTFB

~10ms

Server response

FCP

~0.4s

First paint

LCP

~0.7s

Largest paint

SI

~0.6s

Speed Index

Nguyên nhân điểm cao

Tương tự SSG, ISR có các trang web đã được xây dựng sẵn thành file HTML hoàn chỉnh và máy chủ Next.js chỉ cần đưa nội dung qua cho người dùng. Nội dung luôn được cập nhật mới nhờ cơ chế on-demand revalidation qua webhook.

Kết luận

ISR là minh chứng rõ ràng nhất cho sự ưu việt của kiến trúc Hybrid. Phục vụ nội dung tĩnh ngay lập tức và chỉ âm thầm kết nối Backend để làm mới dữ liệu ở background.

Đánh đổi: Cần setup webhook endpoint, phức tạp hơn SSG.

SSR

Lighthouse Performance: Tốt

SI: ~1.8s · TTFB: ~300ms · FCP: ~0.8s · LCP: ~1.1s

88/100

Filmstrip Timeline (mô phỏng)

0msTrắng
200msTrắng
600msNội dung
1.0sNội dung
1.5s+Hoàn chỉnh

Sau khi gửi yêu cầu, trình duyệt phải đứng chờ máy chủ xử lý xong dữ liệu mới nhận được trang web. User thấy trắng trong thời gian server render.

TTFB

~300ms

Server response

FCP

~0.8s

First paint

LCP

~1.1s

Largest paint

SI

~1.8s

Speed Index

Nguyên nhân điểm bị giảm

Mỗi khi người dùng truy cập, máy chủ Next.js phải gọi API lấy dữ liệu từ Backend và tiến hành render HTML ngay tại thời điểm đó. Quá trình chờ dữ liệu này tạo ra độ trễ, làm chỉ số Speed Index bị kéo dài lên 1.8 giây.

Kết luận

SSR đảm bảo nội dung luôn mới nhất và chính xác ở thời gian thực. Tuy nhiên, tốc độ không còn tức thì và phụ thuộc hoàn toàn vào sức khỏe của máy chủ.

Đánh đổi: Tốc độ phụ thuộc vào server — traffic cao dẫn đến TTFB cao.

CSR

Lighthouse Performance: Tốt

SI: ~2.0s · TTFB: ~5ms · FCP: ~1.2s · LCP: ~2.1s

88/100

Filmstrip Timeline (mô phỏng)

0msTrắng
200msTrắng
600msLoading
1.0sLoading
1.5s+Hoàn chỉnh

Sau khi gửi yêu cầu, trình duyệt lập tức nhận phản hồi HTML rỗng nhưng vẫn chờ thời gian trình duyệt lấy dữ liệu từ server để hiển thị được.

TTFB

~5ms

Server response

FCP

~1.2s

First paint

LCP

~2.1s

Largest paint

SI

~2.0s

Speed Index

Nguyên nhân điểm bị giảm

Tương tự SSR, CSR cần thời gian xây dựng và lấy dữ liệu từ Server trước khi hiển thị nội dung hoàn chỉnh. Khác với SSR, CSR xây dựng dữ liệu hoàn toàn trên trình duyệt người dùng, giảm chi phí Server nhưng user phải nhìn màn hình loading.

Kết luận

Dù trang phản hồi khung giao diện nhanh (TTFB thấp), người dùng vẫn phải nhìn màn hình chờ một khoảng thời gian lâu trước khi thấy nội dung thực sự.

Đánh đổi: TTFB thấp nhưng gây hiểu nhầm — HTML rỗng, nội dung chưa có gì.

Mục 3.3.6

Tổng kết so sánh

ModeScoreTTFBFCPLCPSIDữ liệu
SSG92/100~10ms~0.4s~0.7s~0.6sĐóng băng tại build
ISR92/100~10ms~0.4s~0.7s~0.6sOn-demand qua webhook
SSR88/100~300ms~0.8s~1.1s~1.8sRealtime mỗi request
CSR88/100~5ms~1.2s~2.1s~2.0sRealtime, fetch tại browser

Về tốc độ tải trang

SSG và ISR chiếm ưu thế tuyệt đối với 92/100 nhờ loại bỏ hoàn toàn server compute mỗi request. SSR và CSR cùng đạt 88/100, phải đánh đổi bằng độ trễ lấy và xử lý dữ liệu.

Về tính cập nhật dữ liệu

SSR và CSR đảm bảo real-time nhưng đánh đổi tốc độ. SSG nhanh nhất nhưng dữ liệu cố định. ISR là giải pháp tối ưu: tốc độ ngang SSG, cập nhật on-demand qua webhook mà không rebuild toàn bộ site.

Mục 3.4 — Phương pháp tối ưu khác

Tối ưu hiệu năng bổ sung

Mục 3.4.1

Tối ưu hình ảnh với Next/Image + Sharp

🖼️

Hình ảnh thường chiếm phần lớn dung lượng tải xuống của một trang web. Đây là nơi dễ dàng cải thiện chỉ số LCP (Largest Contentful Paint) nhất.

⚠️ Trước khi tối ưu3,224 KiB

Thẻ HTML

<img /> (truyền thống)

Kết quả

Ảnh gốc kích thước đầy đủ

next.config.mjs — remotePatterns

images: {
  remotePatterns: [{
    protocol: 'https',
    hostname: '*.digitaloceanspaces.com',
  }]
}

Cấu hình này cho phép Sharp lấy và xử lý ảnh an toàn từ DigitalOcean Spaces. Ảnh tự động nén và điều chỉnh kích thước cho phù hợp với màn hình.

💡

priority={i === 0} trên ảnh hero đầu tiên của HomeSlider inject một thẻ<link rel="preload">vào <head> — browser fetch ảnh trước khi render component, cải thiện trực tiếp chỉ số LCP.

Mục 3.4.2

Tối ưu tốc độ lấy dữ liệu — Full Colocation

🚀

Nút thắt cổ chai lớn nhất trong SSR và CSR thường nằm ở tốc độ I/O giữa các phân hệ. Khi Frontend, Backend và Database đặt trên các máy chủ khác nhau, mỗi request phải truyền tải qua Internet công cộng — cộng dồn lại làm tăng mạnh TTFB.

❌ Phân tán (Distributed)

FrontendVercel (US)
↓ ~50–150ms
BackendHeroku (EU)
↓ ~50–150ms
DatabaseMongoDB Atlas (SG)

Tổng: 150–300ms mỗi request

✅ Colocation (Dự án này)

DigitalOcean VPS (SGP)

Next.js Frontend
Express Backend
MongoDB

Giao tiếp qua localhost — không qua Internet

Tổng: ~19.8ms mỗi request

Terminal Coolify — curl đo thời gian truy vấn nội bộ

$ curl -w "\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s" \

http://localhost:5123/api/posts/thao-cam-vien-sai-gon

Connect: 0.003618s (~3.6 ms)

TTFB: 0.019745s (~19.7 ms)

Total: 0.019798s (~19.8 ms)

Đây là yếu tố then chốt giúp các trang SSR và CSR trong đồ án đạt được tốc độ gần với trang tĩnh (SSG/ISR), đảm bảo trải nghiệm người dùng luôn mượt mà.

Mục 3.5 (Bổ sung)

On-demand ISR trong thực tế

So sánh trực quan SSG vs ISR

On-demand Revalidation trong thực tế

🔄

Minh hoạ: Admin chỉnh sửa tên bài viết từ "Thảo Cầm Viên Quận 1 HCM""Thảo Cầm Viên Quận 1 Hồ Chí Minh". Nhấn nút để mô phỏng webhook kích hoạt.

SSGĐóng băng tại build

title:

"Thảo Cầm Viên Quận 1 HCM"

Không bao giờ thay đổi. Dù admin đã sửa, SSG vẫn hiển thị tên cũ cho đến khi npm run build.

ISROn-demand webhook

title:

"Thảo Cầm Viên Quận 1 HCM"

Đang phục vụ bản cached. Chờ webhook từ backend...

Webhook Flow

Admin sửa bàipostRoutes.jsPOST /api/revalidateRegenerate HTMLCache mới

Mục 3.5 — Mô phỏng thực tế

Áp dụng từng Rendering vào đúng vị trí

Thay vì áp dụng một kỹ thuật duy nhất cho toàn bộ dự án, mỗi trang nội dung được chỉ định một cơ chế kết xuất riêng biệt nhằm giải quyết các bài toán cụ thể về tốc độ, khả năng cập nhật dữ liệu và trải nghiệm người dùng.

🏠

Trang chủ & Bài viết

/ và /posts/[slug]
ISR

Lý do lựa chọn

Nếu dùng SSG, nội dung trang chủ sẽ bị cố định. ISR giúp trang chủ vừa có tốc độ phản hồi nhanh, vừa đảm bảo SEO vượt trội và tự động cập nhật khi có bài viết mới.

Cơ chế vận hành

Khi admin thêm hoặc sửa bài viết, backend gửi webhook → Next.js regenerate trang ở background → user tiếp theo nhận bản mới mà không cần rebuild.

📄

Trang thông tin tĩnh

/docs/hybrid, /docs/seo, ...
SSG

Lý do lựa chọn

Các trang nội dung Footer như "Về chúng tôi" hay "Điều khoản" gần như không bao giờ thay đổi. SSG là phương án hiệu quả nhất — loại bỏ hoàn toàn việc gọi Backend hay truy vấn DB mỗi khi có người xem.

Cơ chế vận hành

Toàn bộ nội dung được biên dịch thành file HTML tĩnh một lần khi Build. Khi user nhấn link ở Footer, trình duyệt tải ngay file đã có sẵn trên CDN.

🔍

Trang Tìm kiếm

/search?q=...
SSR

Lý do lựa chọn

Kết quả tìm kiếm phụ thuộc hoàn toàn vào từ khóa người dùng nhập tại thời điểm thực tế. Không thể dùng SSG hay ISR vì không thể xác định trước các tổ hợp tìm kiếm.

Cơ chế vận hành

Mỗi khi user tìm kiếm, yêu cầu gửi đến server. Next.js truy vấn DB, lấy danh sách bài viết khớp từ khóa và render thành HTML hoàn chỉnh trước khi gửi về trình duyệt.

⚙️

Trang Quản trị

/admin/dashboard
CSR

Lý do lựa chọn

Trang admin chứa thông tin riêng tư và yêu cầu tính tương tác cao (thêm, xóa, sửa bài viết). Tối ưu SEO là không cần thiết — CSR tạo ra giao diện mượt mà như Desktop App.

Cơ chế vận hành

Browser tải bộ khung ứng dụng trống. JS gọi API lấy dữ liệu bài viết. Mọi thay đổi cập nhật trực tiếp trên giao diện ngay khi có phản hồi từ Backend mà không tải lại trang.

Tiếp theo

Chương 4: Kết quả & Kết luận

Tổng hợp số liệu đo lường, phân tích kết quả thực nghiệm và rút ra các kết luận về hiệu quả của 4 rendering mode trong môi trường Cloud

Kết quả →