ข้ามไปยังเนื้อหา

Performance & Zero-JS

framework ส่วนใหญ่ทำให้เราต้องไล่ทวง performance คืน: build app เสร็จ ปรากฏว่าช้า แล้วค่อย optimize Astro กลับด้าน — เรา เริ่ม ที่ baseline ที่เร็ว (static HTML, zero JavaScript) และงานของเราคือรักษา baseline นั้นไว้ ทุกกิโลไบต์ของ client JavaScript คืออันที่เราเพิ่มเข้าไปเอง คำถามจึงเป็น “อันนี้จำเป็นต้องส่งไปจริงไหม” เสมอ

บทเรียน islands พูดถึงว่า client directive ทำอะไร ตรงนี้คือมุมมองด้าน performance directive ที่เราเลือกกำหนดว่า JavaScript จะรันเท่าไรและเมื่อไร:

Directiveส่ง & รัน JSใช้เมื่อ
client:loadทันทีตอน page loadinteractivity สำคัญ above-the-fold (พบไม่บ่อย)
client:idleเมื่อ browser ว่างinteractive แต่ไม่จำเป็นต้องทันที
client:visibleเมื่อ scroll เข้ามาในจอisland ที่อยู่ below-the-fold (default ที่ดีที่สุด)
client:mediaเฉพาะที่ breakpointwidget เฉพาะ mobile หรือเฉพาะ desktop

หลักคิด: เอื้อมไปหา client:visible (หรือ client:idle) ก่อน client:load carousel ที่อยู่ลงไปสามจอไม่ต้องใช้ JavaScript ตอน load แรก การ hydrate ด้วย client:visible ทำให้ cost นั้นอยู่นอก critical path การใช้ client:load เกินจำเป็นจะแอบสร้าง cost แบบ “ทุกอย่าง hydrate ตอนแรก” ที่ Astro มีอยู่เพื่อหลีกเลี่ยงกลับมา

flowchart LR
  load["client:load"] --> upfront["JS on the critical path (heaviest)"]
  idle["client:idle"] --> later["JS after first paint"]
  visible["client:visible"] --> onscroll["JS only when scrolled into view (lightest)"]
Directives move JS cost off the critical path

load แรกเร็วเป็นแค่ครึ่งเรื่อง navigation ที่เร็วคืออีกครึ่ง Astro prefetch HTML ของหน้าได้ก่อนที่ user จะคลิก หน้าถัดไปจึงแทบจะทันที เปิดใน config แล้ว Astro จะ prefetch link ตอน hover หรือตอนเข้าจอให้โดย default หรือ opt in ต่อ link แบบชัดเจน:

<a href="/pricing" data-astro-prefetch>Pricing</a>

พอจับคู่กับ <ClientRouter /> prefetch ทำให้ navigation แบบ multi-page รู้สึกเหมือน SPA — HTML อยู่ใน cache ของ browser แล้วตอนคลิก — โดยไม่ต้องมี data layer ฝั่ง client

อย่า optimize ด้วยความรู้สึก ดูสองอย่าง:

  • island ที่เราส่งไป build เว็บแล้วเช็คว่า component ไหนกลายเป็น island จริง (และด้วย directive อะไร) component ที่คิดว่า static แต่ hydrate อยู่คือ JavaScript ที่เสียเปล่า ส่วน client:load ที่ควรเป็น client:visible คือ cost ที่วางผิดที่
  • Lighthouse / Core Web Vitals รัน Lighthouse (ใน Chrome DevTools) กับเว็บที่ build แล้ว จับตา Largest Contentful Paint (มักเป็น hero image — ดูบทเรียน asset), Cumulative Layout Shift (จองพื้นที่ image/font) และ Total Blocking Time (JavaScript ของ island)

baseline ของ Astro มักได้คะแนนเกือบเต็มตั้งแต่แรก เมื่อคะแนนตก สาเหตุมักเป็นหนึ่งใน: image ที่ไม่ optimize, font ที่ทำให้ shift หรือ island ที่ hydrate เร็วกว่าที่จำเป็น

ปรัชญาด้าน performance ของ Astro คืออะไร?
client directive ไหนเป็น default ที่ดีที่สุดสำหรับ island ที่อยู่ below-the-fold?
prefetch ทำอะไร และจับคู่กับ view transitions ยังไง?
คะแนน Lighthouse ตกในหน้า Astro สาเหตุที่น่าจะเป็นที่สุดคืออะไร?