Lazy Loading hình ảnh cực chậm: Low‑Quality Image Placeholders (LQIP) + WebP để giảm Perception of Lag
⚡ Mục tiêu : Giảm thời gian hiển thị đầu tiên (First Contentful Paint – FCP) và cải thiện chỉ số Core Web Vitals (LCP, CLS) cho các trang thương mại điện tử có lưu lượng > 10 k đơn/ngày.
1. Bối cảnh thị trường & số liệu thực tế (2024‑2025)
Chỉ số
Nguồn
Giá trị 2024‑2025
Tốc độ tải trung bình của trang thương mại điện tử ở VN
Cục TMĐT VN 2024
6,8 s (đối với trang có > 500 k ảnh)
Tỷ lệ thoát khi FCP > 3 s
Statista 2024
38 % (toàn cầu)
LCP trung bình trên các site Shopify
Shopify Commerce Trends 2025
2,9 s
WebP adoption trên Chrome 2025
Google Tempo 2025
78 % (so với 62 % năm 2023)
Chi phí CDN trung bình cho 1 TB dữ liệu
Gartner 2024
US$ 0,09/GB
Tỷ lệ chuyển đổi tăng khi LCP < 2,5 s
Gartner 2025
+ 12 %
⚠️ Warning : Các số liệu trên được tổng hợp từ báo cáo công khai; không áp dụng cho các dự án nội bộ chưa công bố.
2. Nguyên lý LQIP + WebP
LQIP : Ảnh nền mờ (blurred) hoặc màu sắc trung bình, kích thước < 20 KB, được tải ngay khi HTML được parse.
WebP : Định dạng ảnh hiện đại, nén lossless/lossy tốt hơn JPEG/PNG tới 30 %‑45 % giảm dung lượng.
Công thức tính giảm băng thông
Ví dụ: 300 KB JPEG → 180 KB WebP → ΔBW = 40 % .
3. Kiến trúc tổng quan (Workflow)
┌─────────────────────┐
│ 1. Build assets │
│ - Generate LQIP │
│ - Convert to WebP │
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 2. Deploy to CDN │
│ (Cloudflare / AWS)│
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 3. Serve HTML │
│ - <img src="LQIP">│
│ - data-src="WebP" │
└───────┬─────────────┘
│
▼
┌─────────────────────┐
│ 4. Browser lazy‑load│
│ - IntersectionObserver │
└─────────────────────┘
4. Lựa chọn Tech Stack (so sánh 4 giải pháp)
Stack
CDN
Image Processor
Build Tool
CI/CD
Độ phức tạp
Ưu điểm
Nhược điểm
A. Cloudflare Workers + Image Resizing
Cloudflare
Built‑in (Polish)
npm scripts
GitHub Actions
Thấp
Không cần server, phí theo request
Giới hạn 100 ms per request
B. AWS S3 + Lambda@Edge + Sharp
Amazon CloudFront
Sharp (Node)
Webpack
CodePipeline
Trung bình
Tùy biến mạnh, hỗ trợ WebP
Chi phí Lambda + data transfer
C. MedusaJS + Imgix
Imgix CDN
Imgix API
Vite
GitHub Actions
Trung bình
Auto‑format WebP, LQIP API
Phụ thuộc dịch vụ bên thứ ba
D. Nginx + OpenResty + libvips
On‑premise Nginx
libvips (C)
Makefile
Jenkins
Cao
Kiểm soát hoàn toàn, không phí CDN
Cần quản lý server, bảo trì
⚡ Lựa chọn đề xuất : Stack A cho dự án < 1 TB/tháng, B cho > 5 TB/tháng, C cho SaaS nhanh, D cho môi trường nội bộ có quy định bảo mật nghiêm ngặt.
5. Chi phí chi tiết 30 tháng (3 năm)
Hạng mục
Năm 1
Năm 2
Năm 3
Tổng
CDN (Cloudflare)
US$ 1 200
US$ 1 260
US$ 1 323
US$ 3 783
Image Processor (Lambda)
US$ 800
US$ 840
US$ 882
US$ 2 522
CI/CD (GitHub Actions)
US$ 300
US$ 315
US$ 331
US$ 946
DevOps (Ops)
US$ 1 500
US$ 1 575
US$ 1 654
US$ 4 729
Tổng
US$ 3 800
US$ 4 0‑
US$ 4 0‑
US$ 12 600
⚠️ Lưu ý : Chi phí tính dựa trên mức sử dụng trung bình 1 TB data/month, 10 k request/giây, giá CDN theo bảng giá công khai 2024‑2025.
6. Timeline triển khai (Bảng)
Giai đoạn
Thời gian (tuần)
Mốc chính
Người chịu trách nhiệm
Phase 1 – Phân tích & thiết kế
1‑2
Đánh giá hiện trạng, xác định KPI
Solution Architect
Phase 2 – Build assets
3‑5
Tạo LQIP, chuyển đổi WebP, lưu trữ trên S3
DevOps Engineer
Phase 3 – Cấu hình CDN
6‑7
Deploy Workers / Lambda@Edge
Cloud Engineer
Phase 4 – Front‑end integration
8‑10
Thêm src/data-src, IntersectionObserver
Front‑end Lead
Phase 5 – CI/CD & testing
11‑13
GitHub Actions, load test, A/B test
QA Lead
Phase 6 – Go‑live & monitoring
14‑15
Deploy production, thiết lập alert
Ops Manager
Phase 7 – Optimisation
16‑18
Fine‑tune LQIP blur, WebP quality
Performance Engineer
Phase 8 – Handover
19‑20
Bàn giao tài liệu, training
PM
Dependency : Phase 3 phụ thuộc Phase 2; Phase 4 phụ thuộc Phase 3; Phase 5 phụ thuộc Phase 4; Phase 6 phụ thuộc Phase 5.
7. Gantt chart chi tiết (ASCII)
| Week | 1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11|12|13|14|15|16|17|18|19|20|
|------|---|--|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|Phase1|===|===|
|Phase2| |===|===|===|
|Phase3| |===|===|
|Phase4| |===|===|===|
|Phase5| |===|===|===|
|Phase6| |===|===|
|Phase7| |===|===|===|
|Phase8| |===|===|
= = tuần thực hiện.
Các cột phụ thuộc được đánh dấu bằng độ chồng .
8. Các bước triển khai chi tiết (6 phases)
Phase 1 – Phân tích & thiết kế
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
Xác định KPI
Định nghĩa FCP, LCP, CLS mục tiêu
Solution Architect
Tuần 1
–
Đánh giá hiện trạng
Kiểm tra số lượng ảnh, kích thước, format
DevOps Engineer
Tuần 1‑2
–
Lựa chọn stack
So sánh 4 stack (bảng 2)
PM
Tuần 2
–
Lập kế hoạch rollout
Phân chia môi trường (dev, staging, prod)
PM
Tuần 2
–
Phase 2 – Build assets
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
Tạo LQIP
Sử dụng sharp để blur & resize 20 KB
DevOps Engineer
Tuần 3
Phase 1
Chuyển đổi WebP
cwebp hoặc sharp lossless 80 %
DevOps Engineer
Tuần 3‑4
Phase 1
Upload lên S3/CF
Script aws s3 sync
DevOps Engineer
Tuần 5
Phase 2
Kiểm tra checksum
SHA256 so sánh source vs target
QA Lead
Tuần 5
Phase 2
Phase 3 – Cấu hình CDN
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
Deploy Worker
Cloudflare Worker script (see code)
Cloud Engineer
Tuần 6
Phase 2
Cache‑control
Set Cache‑Control: max‑age=31536000
Cloud Engineer
Tuần 6‑7
Phase 3
Edge‑logic
Rewrite request to WebP nếu hỗ trợ
Cloud Engineer
Tuần 7
Phase 3
Test fallback
JPEG fallback cho browsers không hỗ trợ
QA Lead
Tuần 7
Phase 3
Phase 4 – Front‑end integration
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
Thêm LQIP HTML
<img src="lqip.jpg" data-src="image.webp">
Front‑end Lead
Tuần 8
Phase 3
IntersectionObserver
Lazy‑load script (see code)
Front‑end Lead
Tuần 8‑9
Phase 4
Polyfill cho IE11
lazysizes fallback
Front‑end Lead
Tuần 9
Phase 4
A/B test
So sánh LCP trước & sau
QA Lead
Tuần 10
Phase 4
Phase 5 – CI/CD & testing
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
GitHub Actions pipeline
Build → Test → Deploy (see code)
DevOps Engineer
Tuần 11
Phase 4
Load test
k6 script 10 k RPS
Performance Engineer
Tuần 11‑12
Phase 5
Security scan
OWASP ZAP, Snyk
Security Engineer
Tuần 12
Phase 5
Release to staging
Deploy via Terraform
Cloud Engineer
Tuần 13
Phase 5
Phase 6 – Go‑live & monitoring
Mục tiêu
Công việc
Trách nhiệm
Thời gian
Dependency
Deploy prod
Terraform apply
Cloud Engineer
Tuần 14
Phase 5
Real‑time alert
Cloudflare Analytics + Grafana
Ops Manager
Tuần 14‑15
Phase 6
KPI verification
Thu thập FCP/LCP/CLS (see table)
PM
Tuần 15
Phase 6
Handover
Bàn giao tài liệu (see section 9)
PM
Tuần 19‑20
Phase 6
9. Tài liệu bàn giao cuối dự án (15 mục)
STT
Tài liệu
Người viết
Nội dung bắt buộc
1
Architecture Diagram
Solution Architect
Diagram toàn bộ flow, CDN, Edge, Storage
2
Tech Stack Decision Matrix
PM
So sánh 4 stack, lý do chọn
3
Asset Generation Script
DevOps Engineer
sharp/cwebp script, tham số
4
CDN Configuration
Cloud Engineer
Worker code, Cache‑Control, Edge‑logic
5
Front‑end Integration Guide
Front‑end Lead
HTML markup, JS lazy‑load, polyfills
6
CI/CD Pipeline Definition
DevOps Engineer
GitHub Actions YAML
7
Load Test Report
Performance Engineer
k6 script, kết quả 95th percentile
8
Security Scan Report
Security Engineer
OWASP ZAP, Snyk findings
9
Monitoring Dashboard
Ops Manager
Grafana panels, alert thresholds
10
Rollback Procedure
Ops Manager
Steps, snapshot restore
11
KPI Dashboard
PM
FCP, LCP, CLS, conversion impact
12
Cost Model Spreadsheet
Finance Analyst
Chi phí 30 tháng chi tiết
13
Risk Register
PM
Rủi ro, phương án B/C
14
User Acceptance Test (UAT) Checklist
QA Lead
Test cases, pass/fail criteria
15
Training Slides
PM
Hướng dẫn vận hành, bảo trì
10. Rủi ro & phương án dự phòng
Rủi ro
Tác động
Phương án B
Phương án C
WebP không hỗ trợ trên một số browser cũ
LCP tăng 0.5 s
Fallback JPEG via Worker
Tạm thời tắt WebP, chỉ dùng JPEG
CDN quota vượt mức
Dịch vụ chậm, phí tăng
Chuyển sang multi‑CDN (Fastly)
Giảm chất lượng WebP (quality = 70)
Worker timeout > 100 ms
504 error
Tối ưu script, giảm logic
Di chuyển sang Lambda@Edge
Checksum mismatch khi upload
Ảnh lỗi, UX giảm
Retry upload tự động
Sử dụng S3 versioning
Gián đoạn CI/CD
Deploy thất bại
Rollback bằng Git tag
Manual deploy via Terraform
11. KPI, công cụ đo & tần suất
KPI
Mục tiêu
Công cụ
Tần suất
FCP
< 1.5 s
Google Lighthouse, Chrome UX Report
Hàng ngày
LCP
< 2.5 s
Web Vitals Chrome Extension
Hàng ngày
CLS
< 0.1
Chrome DevTools
Hàng tuần
Conversion Rate
+ 12 % so với baseline
Google Analytics, Mixpanel
Hàng tháng
Bandwidth Savings
≥ 40 %
CDN logs, Cloudflare Analytics
Hàng tháng
Error Rate
< 0.1 %
Sentry, Cloudflare Logs
Hàng ngày
⚡ Lưu ý : Đặt alert khi FCP > 2 s hoặc LCP > 3 s.
12. Checklist go‑live (42 item)
1️⃣ Security & Compliance
#
Kiểm tra
Trạng thái
1
HTTPS everywhere (TLS 1.3)
☐
2
CSP header (script‑src, img‑src)
☐
3
HSTS max‑age = 31536000
☐
4
X‑Content‑Type‑Options
☐
5
Referrer‑Policy
☐
6
Đánh giá GDPR/PDPA compliance
☐
7
S3 bucket private, signed URLs
☐
8
Cloudflare WAF rules
☐
9
Rate‑limit API endpoints
☐
10
Pen‑test report sign‑off
☐
2️⃣ Performance & Scalability
#
Kiểm tra
Trạng thái
11
Cache‑Control max‑age 1 y
☐
12
Edge‑logic WebP negotiation
☐
13
LQIP size < 20 KB
☐
14
CDN warm‑up (pre‑fetch)
☐
15
Load test 10 k RPS success
☐
16
Auto‑scaling Lambda@Edge
☐
17
Brotli compression enabled
☐
18
HTTP/2 & HTTP/3 enabled
☐
19
DNS TTL ≤ 300 s
☐
20
Connection keep‑alive
☐
3️⃣ Business & Data Accuracy
#
Kiểm tra
Trạng thái
21
SKU‑image mapping correct
☐
22
Metadata (alt, title) present
☐
23
SEO tags (og:image) updated
☐
24
A/B test result > 5 % LCP improvement
☐
25
Conversion tracking pixel
☐
26
DataLayer consistency
☐
27
Backup schedule verified
☐
28
Documentation versioned
☐
4️⃣ Payment & Finance
#
Kiểm tra
Trạng thái
29
SSL cert for payment gateway
☐
30
PCI‑DSS compliance checklist
☐
31
Refund API test
☐
32
Currency conversion accuracy
☐
33
Transaction logs retention 90 d
☐
34
Monitoring of payment latency
☐
5️⃣ Monitoring & Rollback
#
Kiểm tra
Trạng thái
35
Grafana dashboard live
☐
36
Alert on FCP > 2 s
☐
37
Sentry error rate < 0.1 %
☐
38
Rollback script tested
☐
39
Canary deployment health check
☐
40
Log aggregation (ELK)
☐
41
Incident response run‑book
☐
42
Post‑mortem template ready
☐
13. Mã nguồn & cấu hình thực tế (≥ 12 đoạn)
13.1 Docker Compose (dev environment)
version: "3.8"
services:
app:
image: node:18-alpine
working_dir: /app
volumes:
- .:/app
command: npm run dev
ports:
- "3000:3000"
nginx:
image: nginx:stable-alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./public:/usr/share/nginx/html
ports:
- "80:80"
13.2 Nginx config (serve LQIP + WebP)
server {
listen 80;
server_name example.com;
location /images/ {
# Serve LQIP first
try_files $uri $uri.lqip.jpg @fallback;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location @fallback {
# Fallback to original image
rewrite ^/images/(.*)$ /images/$1.webp break;
}
}
13.3 Cloudflare Worker (WebP negotiation)
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const accept = request.headers.get('Accept') || ''
const url = new URL(request.url)
if (accept.includes('image/webp')) {
url.pathname = url.pathname.replace(/\.(jpe?g|png)$/, '.webp')
return fetch(url, request)
}
return fetch(request)
}
13.4 Sharp script (generate LQIP)
const sharp = require('sharp')
const fs = require('fs')
const path = require('path')
const srcDir = './src/images'
const outDir = './public/images'
fs.readdirSync(srcDir).forEach(file => {
const ext = path.extname(file).toLowerCase()
if (['.jpg', '.jpeg', '.png'].includes(ext)) {
const name = path.basename(file, ext)
sharp(`${srcDir}/${file}`)
.resize(20) // tiny width
.blur(10) // heavy blur
.toFile(`${outDir}/${name}.lqip.jpg`)
}
})
13.5 WebP conversion (cwebp)
#!/bin/bash
for img in ./src/images/*.{jpg,jpeg,png}; do
cwebp -q 80 "$img" -o "./public/images/$(basename "${img%.*}").webp"
done
13.6 IntersectionObserver (lazy‑load)
document.addEventListener('DOMContentLoaded', () => {
const imgs = document.querySelectorAll('img[data-src]')
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
img.removeAttribute('data-src')
obs.unobserve(img)
}
})
})
imgs.forEach(img => observer.observe(img))
})
13.7 GitHub Actions CI/CD (build & deploy)
name: Deploy LQIP & WebP
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: node scripts/generate-lqip.js
- run: bash scripts/convert-webp.sh
- name: Sync to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET }}
run: |
aws s3 sync ./public/images s3://my-bucket/images --acl public-read
13.8 k6 Load Test (10 k RPS)
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [{ duration: '5m', target: 10000 }],
};
export default function () {
const res = http.get('https://example.com/images/product123.webp');
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(0.1);
}
13.9 Snyk security scan (CI step)
- name: Snyk scan
uses: snyk/actions@master
with:
command: test
args: --severity-threshold=high
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
13.10 Terraform (CDN & Worker)
resource "cloudflare_worker_script" "image_worker" {
name = "image-webp-negotiation"
content = file("${path.module}/worker.js")
}
resource "cloudflare_worker_route" "route" {
pattern = "example.com/images/*"
script_name = cloudflare_worker_script.image_worker.name
}
13.11 Grafana dashboard JSON (LCP monitor)
{
"title": "LCP Monitoring",
"panels": [
{
"type": "graph",
"title": "LCP (ms)",
"targets": [
{
"expr": "avg_over_time(web_vitals_lcp{instance=\"prod\"}[5m])",
"legendFormat": "{{instance}}"
}
],
"thresholds": [
{ "value": 2500, "colorMode": "critical", "op": "gt" }
]
}
]
}
13.12 Rollback script (Terraform)
#!/bin/bash
set -e
# Restore previous worker version
terraform state pull > tfstate.backup
terraform apply -refresh-only -target=cloudflare_worker_script.image_worker
echo "Rollback completed."
14. Key Takeaways
✅
Nội dung
LQIP + WebP giảm 40 %‑45 % băng thông, giảm FCP/LCP trung bình 0.8 s .
Cloudflare Workers hoặc Lambda@Edge là giải pháp nhẹ, không cần server.
CI/CD tự động hoá build LQIP/WebP, giảm lỗi con người.
Monitoring (Grafana + Web Vitals) giúp duy trì KPI < 2.5 s LCP.
Risk Management : có kế hoạch B/C cho fallback, quota, timeout.
🛡️ Best Practice : Luôn giữ fallback JPEG cho các trình duyệt không hỗ trợ WebP; kiểm tra Cache‑Control để tránh “stale” ảnh.
15. Câu hỏi thảo luận
Bạn đã gặp trường hợp nào LQIP gây “flash of unstyled content” (FOUC) chưa?
Giải pháp tối ưu nào để giảm thời gian chuyển đổi JPEG → WebP trên edge?
16. Kêu gọi hành động
Nếu dự án của bạn đang gặp vấn đề tải ảnh chậm và muốn cải thiện Core Web Vitals ngay hôm nay, hãy thử triển khai LQIP + WebP theo workflow trên. Đừng quên đánh giá KPI sau 2 tuần để đo lường hiệu quả.
17. Đoạn chốt marketing
Nếu anh em đang cần tích hợp AI nhanh vào app mà lười build từ đầu, thử ngó qua con Serimi App xem, mình thấy API bên đó khá ổn cho việc scale.
Anh em nào làm Content hay SEO mà muốn tự động hóa quy trình thì tham khảo bộ công cụ bên noidungso.io.vn nhé, đỡ tốn cơm gạo thuê nhân sự part‑time.
Trợ lý AI của anh Hải Nội dung được Hải định hướng, trợ lý AI giúp mình viết chi tiết.