CSS
Advanced & Best Practices
Performance Optimization
เรียนรู้การเขียน CSS ที่ไม่ทำให้เว็บช้า — ลดโค้ดซ้ำ ใช้ effects พอดี และออกแบบ UI ที่สวยโดยไม่แลกกับความลื่น
หัวข้อนี้คืออะไร
CSS Performance Optimization คือการเขียน CSS ให้หน้าเว็บ แสดงผลเร็ว เลื่อนหน้าลื่น และไม่ใช้ทรัพยากรมากเกินจำเป็น ในมุมของ CSS และ frontend ปัญหา performance มักเกิดจาก: - CSS ที่ซ้ำและบวมโดยไม่จำเป็น - Effects หนัก เช่น blur, shadow ซ้อนหลายชั้น, animation ทุก element - Background image ขนาดใหญ่โดยไม่ได้ optimize - การสร้าง layer ใหม่มากเกินไปด้วย transform, filter, will-change Performance ที่ดีไม่ได้แปลว่า "ไม่สวย" แต่คือการตัดสินใจอย่างชาญฉลาดว่าจะใช้ทรัพยากรกับสิ่งไหน และงดสิ่งที่ให้ผลน้อยแต่แลกต้นทุนสูง
/* ❌ หนักเกินจำเป็น */
.card {
box-shadow: 0 0 30px rgba(0,0,0,0.5),
0 0 60px rgba(0,0,0,0.3),
0 0 90px rgba(0,0,0,0.1);
filter: blur(0px) brightness(1.1) contrast(1.05) saturate(1.2);
backdrop-filter: blur(20px) saturate(180%);
animation: pulse 1s infinite, glow 2s infinite, shake 3s infinite;
}
/* ✅ เบาและได้ผลลัพธ์ดีพอกัน */
.card {
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 8px 24px rgba(0,0,0,0.18);
}ทำไมหัวข้อนี้สำคัญ
ผู้ใช้จะออกจากเว็บถ้าโหลดนานกว่า 3 วินาที และถ้าการเลื่อนหน้าไม่ลื่น (ต่ำกว่า 60fps) ผู้ใช้จะรู้สึกได้ทันทีว่าเว็บ "หนัก" Google ใช้ Core Web Vitals ซึ่งรวมถึงความเร็วในการ render เป็นปัจจัยจัดอันดับ SEO ด้วย ดังนั้น performance ไม่ใช่แค่เรื่อง UX แต่ยังส่งผลต่อการค้นหาโดยตรง ที่สำคัญสำหรับนักพัฒนา: CSS ที่บวมและซับซ้อนทำให้ทีมดูแลรักษายาก เพิ่ม feature ช้า และเกิดบั๊กจาก specificity ซับซ้อนบ่อยขึ้น performance ที่ดีจึงเริ่มจากนิสัยการเขียน CSS ที่ดีตั้งแต่ต้น
ตัวอย่างจากชีวิตจริง
ลองสังเกตว่าเว็บข่าวหรือ landing page บางแห่งเลื่อนหน้าแล้วรู้สึก "สะดุด" หรือรูปภาพโหลดช้า — นั่นมักมาจาก background-image ขนาดใหญ่, animation ที่ทำงานตลอดเวลา หรือ CSS filter บน element ที่เห็นอยู่ตลอด ในทางกลับกัน Stripe, Linear, Vercel — เว็บที่ดีใน UI แต่ลื่นมาก ใช้ shadow เพียงชั้นเดียว animation ที่มีจุดประสงค์ชัดเจน และ background ที่ไม่หนัก ตัวอย่างใกล้ตัว: ถ้าเปิด DevTools แล้วดูแท็บ Performance พบว่า animation ที่ใช้ top/left เปลี่ยนทุก frame จะ trigger reflow ทุกครั้ง ส่วน animation ที่ใช้ transform: translate() จะ smooth กว่ามากเพราะ browser คำนวณบน GPU แทน
แนวคิดหลักที่ต้องเข้าใจ
Browser แสดงผลหน้าเว็บผ่าน 3 กระบวนการหลัก: 1. Layout (Reflow) — คำนวณตำแหน่งและขนาดทุก element ถ้าแก้ properties เช่น width, height, margin, padding, top, left จะ trigger กระบวนการนี้ ซึ่งหนักที่สุด 2. Paint — วาดสีและ visual เช่น background, border, shadow ถ้าแก้ color, box-shadow จะ trigger กระบวนการนี้ หนักปานกลาง 3. Composite — รวม layer ขั้นสุดท้าย properties เช่น transform, opacity ทำงานที่ขั้นนี้เพียงอย่างเดียว เบาที่สุดและ smooth ที่สุด กฎทอง: ถ้าต้องการ animation ให้ใช้ transform และ opacity เท่านั้น หลีกเลี่ยงการ animate width, height, top, left
| กระบวนการ | Properties ที่ trigger | ต้นทุน |
|---|---|---|
| Layout (Reflow) | width, height, margin, padding, top, left, font-size | สูงมาก |
| Paint | color, background, box-shadow, border | ปานกลาง |
| Composite | transform, opacity | ต่ำ (GPU) |
การทำงานทีละขั้นตอน
- ตรวจหา CSS ที่ซ้ำ — ค้นหา property เดิมที่เขียนซ้ำในหลาย selector แล้วรวมเป็น shared class หรือ CSS Variable
- ประเมิน effects ทุกชิ้น — ถามตัวเองว่า shadow, filter, blur, animation นี้ให้คุณค่าอะไรกับผู้ใช้ ถ้าตอบไม่ได้ชัด ให้ลดหรือตัดออก
- Animation ให้ใช้ transform และ opacity เท่านั้น — ถ้าต้องการขยาย ให้ใช้ transform: scale() ถ้าต้องการเลื่อน ให้ใช้ transform: translate()
- Background image ให้ระบุขนาดที่เหมาะสม — ใช้ background-size: cover พร้อม object-fit บน img แทน background-image บน div ถ้าทำได้
- ใช้ will-change อย่างระวัง — will-change: transform บอก browser ล่วงหน้าว่า element นี้จะ animate แต่อย่าใส่ทุก element เพราะสร้าง layer เพิ่มทุกตัว
ตัวอย่างที่ 1 — CSS ซ้ำ vs จัดระบบแล้ว
โค้ดด้านซ้ายเขียน padding และ border-radius ซ้ำใน 3 ที่ ถ้าต้องการเปลี่ยนค่าต้องตามแก้ทุกจุด โค้ดด้านขวาใช้ shared class และ CSS Variables ทำให้เปลี่ยนที่เดียวทุกที่อัปเดต
ตัวอย่างที่ 2 — UI Effect เยอะเกิน vs พอดี
Effects หลายชั้นพร้อมกันไม่ได้ทำให้ดูดีขึ้นเสมอไป แต่ทำให้ browser ทำงานหนักขึ้นมาก ตัวอย่างนี้แสดง card สองแบบที่ให้ความรู้สึกสวยใกล้เคียงกัน แต่ต้นทุนต่างกันมาก
ตัวอย่างที่ 3 — Background Image หนัก vs สมเหตุสมผล
background-image บน div ใหญ่ที่ไม่ optimize เป็นปัญหา performance อันดับต้น ๆ ตัวอย่างนี้แสดงวิธีทำ hero section ที่ดูดีโดยไม่ต้องพึ่ง background image หนัก
จุดที่ผู้เริ่มต้นมักสับสน
- will-change ไม่ใช่ยาวิเศษ — หลายคนใส่ will-change: transform ทุก element คิดว่าจะเร็วขึ้น แต่ความจริง will-change สร้าง GPU layer ใหม่ทุกตัว ถ้าใช้มากเกินจะกินหน่วยความจำและช้าลง ใช้เฉพาะ element ที่จะ animate จริง ๆ
- transition กับ animation ต่างกันในแง่ performance — transition ทำงานเฉพาะเมื่อค่าเปลี่ยน (เช่น hover) animation แบบ infinite loop ทำงานตลอดเวลา animation ที่ไม่จำเป็นจริง ๆ ควรใช้ hover แทน
- CSS selector ยิ่งซับซ้อนยิ่งช้า — selector อย่าง div > ul li:nth-child(odd) a.link ทำให้ browser ต้องประมวลผลมากกว่า .nav-link มาก ในโปรเจกต์ขนาดใหญ่ selector ซับซ้อนสะสมกันได้เยอะ
- ไม่ต้องกังวลทุกอย่างตั้งแต่ต้น — สำหรับโปรเจกต์เล็กหรือระหว่างเรียน ให้โฟกัสที่ความถูกต้องก่อน แล้วค่อย optimize เมื่อพบปัญหาจริง premature optimization คือการเสียเวลากับสิ่งที่อาจไม่ใช่ bottleneck จริง
เปรียบเทียบ Properties ตาม Performance Cost
| การเปลี่ยนแปลง | Trigger | ใช้แทนด้วย |
|---|---|---|
| width / height | Reflow + Paint + Composite | transform: scale() |
| top / left | Reflow + Paint + Composite | transform: translate() |
| margin / padding | Reflow + Paint + Composite | gap หรือ transform |
| background-color | Paint + Composite | ยังใช้ได้ เบาพอ |
| box-shadow | Paint + Composite | ลดจำนวนชั้น |
| filter / backdrop-filter | Paint + Composite | ใช้เท่าที่จำเป็น |
| opacity | Composite only | ✅ เบาที่สุด |
| transform | Composite only | ✅ เบาที่สุด |
สรุปท้ายบทแบบจำง่าย
CSS ที่ดีไม่ใช่แค่สวย แต่ต้องไม่ทำให้ผู้ใช้รอ
- ลด CSS ซ้ำ — ใช้ shared class และ CSS Variables แทนการก็อปโค้ด
- Animation ใช้ transform และ opacity เท่านั้น — หลีกเลี่ยง top, left, width, height
- Effects ให้ใช้เมื่อมีเหตุผล — shadow 1 ชั้น + hover transition ดีกว่า 3 ชั้น + animation ตลอดเวลา
- will-change ใช้น้อยและเฉพาะจุด — ไม่ใช่ใส่ทุก element
- Optimize เมื่อพบปัญหาจริง — อย่า optimize ก่อนเวลา แต่ก็อย่าสร้างนิสัยเลวตั้งแต่ต้น
Lab 1 — ลด CSS ซ้ำด้วย Shared Class
CSS ปัจจุบันมี padding, border-radius, และ font-family ซ้ำใน 3 rules ให้ refactor โดยสร้าง .panel class ที่รวม property ซ้ำเหล่านั้น แล้วเพิ่ม class="panel" ให้ทุก element ใน HTML
Lab 2 — ปรับ Animation ให้ใช้ transform แทน top
animation ปัจจุบันใช้ top เพื่อเลื่อน element ซึ่ง trigger Reflow ทุก frame ให้แก้ให้ใช้ transform: translateY() แทน เพื่อให้ animation ทำงานที่ Composite layer เท่านั้น
Lab 3 — Refactor หน้าเล็ก ๆ ให้เบาลงและ Maintainable
หน้านี้มีปัญหา 3 อย่าง: (1) padding ซ้ำใน .article และ .sidebar, (2) box-shadow 3 ชั้นบน .card, (3) animation infinite บน .badge ที่ไม่จำเป็น ให้แก้ทั้ง 3 จุดโดยสร้าง .surface class, ลด shadow เหลือ 1 ชั้น, และเปลี่ยน animation เป็น transition