ในยุคที่ผู้ใช้คาดหวังว่าเว็บจะโหลดภายใน 2-3 วินาที Web Performance Optimization ไม่ใช่ทางเลือกอีกต่อไป แต่เป็นสิ่งจำเป็น Google ใช้ Core Web Vitals เป็นปัจจัยในการจัดอันดับ SEO และงานวิจัยแสดงว่าเว็บที่ช้าเพียง 1 วินาทีอาจทำให้ Conversion ลดลงถึง 7%
บทความนี้จะครอบคลุมทุกแง่มุมของ Web Performance ตั้งแต่ Core Web Vitals, การวัดผล, Image/JS/CSS Optimization, Caching, Rendering Patterns ไปจนถึงการ Monitor ใน Production
ทำไม Web Performance ถึงสำคัญ?
1. User Experience (UX)
- 53% ของผู้ใช้มือถือจะออกจากเว็บที่โหลดนานกว่า 3 วินาที
- เว็บที่เร็วทำให้ผู้ใช้รู้สึกว่าบริการมีคุณภาพ
- ลด Bounce Rate และเพิ่ม Time on Page
2. SEO Ranking
- Google ใช้ Core Web Vitals เป็น Ranking Factor ตั้งแต่ 2021
- เว็บที่เร็วได้เปรียบในการแข่งขัน Search Results
- Page Experience เป็นส่วนหนึ่งของ Algorithm
3. Conversion Rate
- Amazon พบว่าทุก 100ms ที่เร็วขึ้น Revenue เพิ่ม 1%
- Walmart เพิ่ม Conversion 2% ต่อทุก 1 วินาทีที่เร็วขึ้น
- Mobile e-commerce ได้รับผลกระทบมากที่สุด
Core Web Vitals คืออะไร?
Core Web Vitals คือชุดเมตริกจาก Google ที่วัดประสบการณ์ผู้ใช้จริง 3 ด้าน:
| Metric | วัดอะไร | Good | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | เวลาที่ Content หลักแสดงผล | ≤ 2.5s | > 4.0s |
| INP (Interaction to Next Paint) | ความเร็วตอบสนองต่อ Input | ≤ 200ms | > 500ms |
| CLS (Cumulative Layout Shift) | ความเสถียรของ Layout | ≤ 0.1 | > 0.25 |
LCP — Largest Contentful Paint
LCP วัดเวลาที่ Element ใหญ่ที่สุดในหน้าแสดงผลเสร็จ (เช่น Hero Image, H1 Text Block) สาเหตุ LCP ช้า:
- Server Response Time ช้า (TTFB สูง)
- Render-blocking Resources (CSS, JS)
- Resource Load Time (รูปภาพใหญ่)
- Client-side Rendering
INP — Interaction to Next Paint
INP แทนที่ FID ตั้งแต่ปี 2024 วัดความเร็วในการตอบสนองต่อ User Interaction ทั้งหมด (Click, Tap, Keyboard) ไม่ใช่แค่ครั้งแรก
CLS — Cumulative Layout Shift
CLS วัดว่า Layout ขยับไปมาขนาดไหนขณะโหลด สาเหตุหลัก:
- รูปภาพ/วิดีโอไม่กำหนดขนาด
- Font ที่โหลดช้าทำให้ Text เปลี่ยนขนาด
- Content แทรกเข้ามาแบบ Dynamic
- Ad/Iframe ที่ไม่กำหนดพื้นที่
เครื่องมือวัด Performance
1. Google Lighthouse
# ใช้ผ่าน Chrome DevTools → Lighthouse tab
# หรือ CLI
npm install -g lighthouse
lighthouse https://example.com --output html --output-path report.html
# ใน CI/CD
npx lighthouse-ci https://example.com --preset=desktop
2. PageSpeed Insights
เข้าที่ pagespeed.web.dev ใส่ URL แล้ววิเคราะห์ ได้ทั้ง Lab Data (จำลอง) และ Field Data (ข้อมูลผู้ใช้จริงจาก CrUX)
3. WebPageTest
เครื่องมือ Open Source ที่ให้รายละเอียดสุดของ Waterfall, Filmstrip, Video Comparison เลือกทดสอบจากหลาย Location และ Connection Speed
4. Chrome DevTools Performance Tab
// วัด Performance ใน Code
performance.mark('start-render');
// ... render logic ...
performance.mark('end-render');
performance.measure('render-time', 'start-render', 'end-render');
// Web Vitals API
import {onLCP, onINP, onCLS} from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
Image Optimization — ลดขนาดรูปภาพ
รูปภาพมักเป็น Resource ที่ใหญ่ที่สุดในหน้าเว็บ การ Optimize รูปภาพมีผลต่อ LCP โดยตรง
Modern Image Formats
| Format | ลดขนาด | Browser Support | เหมาะกับ |
|---|---|---|---|
| WebP | 25-35% vs JPEG | 96%+ | ใช้งานทั่วไป |
| AVIF | 50% vs JPEG | 92%+ | คุณภาพสูง ขนาดเล็ก |
| JPEG XL | 60% vs JPEG | จำกัด | อนาคต |
<!-- Responsive Images with Modern Formats -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Description"
width="800" height="600"
loading="lazy"
decoding="async">
</picture>
<!-- Responsive Sizes -->
<img srcset="image-400.webp 400w,
image-800.webp 800w,
image-1200.webp 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 900px) 800px,
1200px"
src="image-800.webp"
alt="Description"
loading="lazy">
Lazy Loading
<!-- Native Lazy Loading -->
<img src="photo.webp" loading="lazy" alt="Photo">
<!-- LCP Image ไม่ควร lazy load! -->
<img src="hero.webp" fetchpriority="high" alt="Hero">
<!-- Intersection Observer (Custom) -->
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
</script>
fetchpriority="high" และห้ามใช้ loading="lazy" ส่วนรูปที่อยู่ Below the fold ให้ใช้ loading="lazy" เสมอ
JavaScript Optimization
Code Splitting
// Dynamic Import — โหลดเฉพาะเมื่อต้องใช้
const module = await import('./heavy-module.js');
// React.lazy + Suspense
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
// Next.js Dynamic Import
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('./Chart'), { ssr: false });
Tree Shaking
// Bad — Import ทั้ง Library
import _ from 'lodash'; // ~70KB gzipped
// Good — Import เฉพาะที่ใช้
import debounce from 'lodash/debounce'; // ~1KB
// Good — ใช้ ES Module
import { debounce } from 'lodash-es';
Script Loading Strategies
<!-- Blocking (ช้าที่สุด) -->
<script src="app.js"></script>
<!-- Async — โหลดพร้อม HTML, รันทันทีที่โหลดเสร็จ -->
<script src="analytics.js" async></script>
<!-- Defer — โหลดพร้อม HTML, รันหลัง HTML parse เสร็จ -->
<script src="app.js" defer></script>
<!-- Module — defer โดย default -->
<script type="module" src="app.mjs"></script>
CSS Optimization
Critical CSS
<!-- Inline Critical CSS -->
<style>
/* CSS ที่จำเป็นสำหรับ Above-the-fold content */
body { margin: 0; font-family: sans-serif; }
.hero { height: 100vh; display: flex; align-items: center; }
</style>
<!-- โหลด CSS ที่เหลือแบบ Non-blocking -->
<link rel="preload" href="styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
CSS Purging
// tailwind.config.js — PurgeCSS built-in
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
// Tailwind จะลบ Class ที่ไม่ได้ใช้ออกอัตโนมัติ
};
// PurgeCSS standalone
// npm install purgecss
npx purgecss --css styles.css --content index.html --output purged.css
CSS Containment
/* บอก Browser ว่า Element นี้ไม่กระทบ Layout อื่น */
.card {
contain: layout style paint;
content-visibility: auto;
contain-intrinsic-size: 200px 300px;
}
Font Optimization
/* font-display: swap — แสดง Fallback ก่อน โหลด Font ทีหลัง */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap;
}
/* Preload Font — โหลดเร็วขึ้น */
<link rel="preload" href="font.woff2" as="font"
type="font/woff2" crossorigin>
/* Font Subset — ใช้เฉพาะ Characters ที่ต้องการ */
/* unicode-range ช่วยลดขนาดได้มาก */
@font-face {
font-family: 'CustomFont';
src: url('font-latin.woff2') format('woff2');
unicode-range: U+0000-00FF;
}
font-display: optional สำหรับ Font ที่ไม่สำคัญ — Browser จะไม่แสดง FOUT (Flash of Unstyled Text) เลย ถ้าโหลดไม่ทันก็ใช้ Fallback ตลอด
Caching Strategies
Browser Caching (HTTP Headers)
# Nginx Configuration
location ~* \.(js|css|png|jpg|webp|avif|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
CDN Caching
ใช้ CDN (Cloudflare, AWS CloudFront, Fastly) เพื่อ Cache Static Assets ใกล้ผู้ใช้:
- ลด Latency ด้วย Edge Servers ทั่วโลก
- ลด Load บน Origin Server
- Automatic Image Optimization (Cloudflare Polish)
- Cache Purge เมื่อ Deploy ใหม่
Service Worker Caching
// sw.js — Cache-First Strategy
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request).then((response) => {
const clone = response.clone();
caches.open('v1').then((cache) => cache.put(event.request, clone));
return response;
});
})
);
});
// Workbox (Google Library สำหรับ Service Worker)
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
precacheAndRoute(self.__WB_MANIFEST);
registerRoute(
({request}) => request.destination === 'image',
new StaleWhileRevalidate({ cacheName: 'images' })
);
HTTP/2 และ HTTP/3
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Multiplexing | ไม่มี | มี | มี |
| Header Compression | ไม่มี | HPACK | QPACK |
| Server Push | ไม่มี | มี | มี |
| Protocol | TCP | TCP | QUIC (UDP) |
| Head-of-Line Blocking | มี | TCP Level | ไม่มี |
# Nginx — เปิด HTTP/2
server {
listen 443 ssl;
http2 on;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
Resource Hints
<!-- Preload — โหลดทรัพยากรสำคัญก่อน -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero.webp" as="image">
<!-- Prefetch — โหลดล่วงหน้าสำหรับหน้าถัดไป -->
<link rel="prefetch" href="/next-page.html">
<link rel="prefetch" href="data.json" as="fetch">
<!-- Preconnect — สร้าง Connection ล่วงหน้า -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<!-- DNS Prefetch — Resolve DNS ล่วงหน้า -->
<link rel="dns-prefetch" href="https://analytics.example.com">
Rendering Patterns
SSR — Server-Side Rendering
Render HTML บน Server ส่ง HTML สมบูรณ์ไปยัง Client ดีสำหรับ LCP และ SEO
// Next.js SSR
export async function getServerSideProps() {
const data = await fetchData();
return { props: { data } };
}
SSG — Static Site Generation
สร้าง HTML ตอน Build Time เร็วที่สุดเพราะเป็น Static Files
// Next.js SSG
export async function getStaticProps() {
const data = await fetchData();
return { props: { data }, revalidate: 3600 }; // ISR: Rebuild ทุก 1 ชม.
}
ISR — Incremental Static Regeneration
ผสมผสาน SSG + SSR: Serve Static แต่ Regenerate เมื่อหมดอายุ ดีสำหรับ Content ที่เปลี่ยนบ่อยปานกลาง
Streaming SSR
// React 18 Streaming
import { renderToPipeableStream } from 'react-dom/server';
app.get('/', (req, res) => {
const stream = renderToPipeableStream(<App />, {
onShellReady() {
res.statusCode = 200;
stream.pipe(res);
}
});
});
Bundle Analysis
# Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer
# package.json
"scripts": {
"analyze": "ANALYZE=true next build"
}
# Next.js — next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({});
# Source Map Explorer (alternative)
npx source-map-explorer dist/main.js
Performance Budgets
กำหนดเพดาน Performance เพื่อป้องกันไม่ให้เว็บช้าลง:
// webpack.config.js
module.exports = {
performance: {
maxAssetSize: 250000, // 250 KB per asset
maxEntrypointSize: 500000, // 500 KB total entry
hints: 'error' // Fail build if exceeded
}
};
// Lighthouse CI Budget (lighthouserc.js)
module.exports = {
ci: {
assert: {
assertions: {
'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-byte-weight': ['warn', { maxNumericValue: 500000 }],
}
}
}
};
React/Next.js Specific Optimizations
// 1. React.memo — ป้องกัน Re-render ที่ไม่จำเป็น
const MemoizedComponent = React.memo(function MyComponent({ data }) {
return <div>{data.name}</div>;
});
// 2. useMemo / useCallback
const expensiveResult = useMemo(() => computeExpensive(data), [data]);
const handleClick = useCallback(() => doSomething(id), [id]);
// 3. Next.js Image Component
import Image from 'next/image';
<Image src="/hero.jpg" width={1200} height={600}
priority // สำหรับ LCP image
placeholder="blur"
blurDataURL="data:image/..." />
// 4. Next.js Script Component
import Script from 'next/script';
<Script src="https://analytics.js" strategy="lazyOnload" />
// 5. React Server Components (RSC)
// Default ใน Next.js 13+ App Router
// Component ที่ไม่มี interactivity ถูก Render บน Server เท่านั้น
// ลดขนาด Client Bundle อย่างมาก
Monitoring ใน Production
Real User Monitoring (RUM)
// web-vitals library
import { onLCP, onINP, onCLS } from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
page: window.location.pathname
});
navigator.sendBeacon('/api/vitals', body);
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
Synthetic Monitoring
ใช้เครื่องมืออัตโนมัติวัด Performance เป็นประจำ:
- Lighthouse CI — รันใน CI/CD ทุก Commit
- SpeedCurve — Dashboard ติดตาม Performance Trends
- Calibre — Budget Alerts เมื่อ Performance ตก
Performance CI/CD Integration
# GitHub Actions — Lighthouse CI
name: Performance Check
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci && npm run build
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000/
http://localhost:3000/blog
budgetPath: ./budget.json
uploadArtifacts: true
Performance Optimization Checklist
| หมวด | Action | ผลกระทบ |
|---|---|---|
| Images | ใช้ WebP/AVIF, Responsive, Lazy Load | สูงมาก |
| JavaScript | Code Split, Tree Shake, Defer | สูงมาก |
| CSS | Critical CSS, Purge, Containment | สูง |
| Fonts | font-display, Preload, Subset | ปานกลาง |
| Caching | Browser Cache, CDN, Service Worker | สูงมาก |
| Protocol | HTTP/2+, Compression (Brotli) | ปานกลาง |
| Rendering | SSR/SSG, Streaming | สูง |
| Monitoring | RUM, Lighthouse CI, Budgets | ป้องกัน Regression |
สรุป
Web Performance Optimization ไม่ใช่งานที่ทำครั้งเดียวแล้วจบ แต่เป็นกระบวนการต่อเนื่อง ที่ต้อง Measure, Optimize, Monitor วนไป เริ่มต้นจากการวัด Core Web Vitals ด้วย Lighthouse แล้ว Focus ที่ Impact สูงสุดก่อน: Images, JavaScript, และ Caching
ในปี 2026 การทำเว็บเร็วไม่ใช่แค่เรื่องเทคนิค แต่เป็นเรื่อง Business ทุก Millisecond ที่เร็วขึ้นคือ Conversion ที่มากขึ้น ใช้ Performance Budget ป้องกัน Regression และ Integrate เข้า CI/CD เพื่อให้ทีมทั้งหมดรักษามาตรฐานเดียวกัน
