Streaming และ Suspense
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”streaming ให้ server ส่งส่วนที่พร้อมแล้วของหน้า — คือ static shell — ออกไปทันที แล้วค่อยดันส่วนที่ช้ากว่าตามมาทาง connection เดียวกันเมื่อ render เสร็จ
static shell stream ออกมาก่อน
หัวข้อที่มีชื่อว่า “static shell stream ออกมาก่อน”ถ้าไม่มี streaming server จะกั๊ก response ทั้งก้อนไว้จนกว่าทุก await จะ resolve ดังนั้นชิ้นที่ data ช้าที่สุดจะเป็นตัวกำหนด time to first byte streaming ตัดหน้าออกเป็น chunk — shell (layout, heading, อะไรก็ตามที่ไม่รอ data) ออกจาก server ก่อน ส่วนที่ช้าตามมาเมื่อ resolve
browser paint shell ทันที ผู้ใช้จึงเห็นหน้าจริงในขณะที่ data ยังโหลดอยู่เบื้องหลัง
loading.tsx คือ Suspense boundary อัตโนมัติ
หัวข้อที่มีชื่อว่า “loading.tsx คือ Suspense boundary อัตโนมัติ”วาง loading.tsx ไว้ข้าง ๆ page.tsx แล้ว Next.js จะห่อทั้ง segment นั้นด้วย Suspense boundary ให้เอง ระหว่างที่ page รอ data อยู่ loading UI จะโชว์ทันที
// app/feed/loading.tsx — shown instantly while page.tsx awaits its dataexport default function Loading() { return <p>Loading your feed…</p>;}คุณไม่ต้องเขียน <Suspense> เองตรงนี้ — file convention คือ boundary เอง segment จะ stream เข้ามาเมื่อ data พร้อม
manual Suspense stream เฉพาะส่วนที่ช้า
หัวข้อที่มีชื่อว่า “manual Suspense stream เฉพาะส่วนที่ช้า”เมื่อมีแค่ region เดียวของหน้าที่ช้า ให้ห่อ region นั้นด้วย <Suspense> ส่วนที่เหลือของหน้าจะ render ทันที และเฉพาะ component ที่ช้าเท่านั้นที่ stream เข้ามา
import { Suspense } from 'react';
async function SlowComments() { const comments = await fetch('https://api.example.com/comments', { cache: 'no-store', }).then((r) => r.json()); return <ul>{comments.map((c) => <li key={c.id}>{c.text}</li>)}</ul>;}
export default function FeedPage() { return ( <section> <h1>Your feed</h1> <p>This heading and text ship in the shell, immediately.</p> <Suspense fallback={<p>Loading comments…</p>}> <SlowComments /> </Suspense> </section> );}heading paint ทันที ส่วน SlowComments stream เข้ามาแล้วแทนที่ fallback เมื่อ fetch resolve
ทำไม streaming ช่วยเรื่อง perceived performance
หัวข้อที่มีชื่อว่า “ทำไม streaming ช่วยเรื่อง perceived performance”streaming แยก “first paint” ออกจาก “data ที่ช้าที่สุด” มีสองข้อดีที่ตามมา —
- TTFB ดีขึ้น — shell ออกจาก server โดยไม่รอ data request ใด ๆ byte แรกจึงมาถึงเร็ว
- perceived performance ดีขึ้น — ผู้ใช้อ่าน layout จริงได้ระหว่างที่ region ช้ากำลังเติมเข้ามา แทนที่จะจ้องหน้าจอว่างจนกว่าทุกอย่างจะพร้อม
flowchart LR A[Request] --> B[Server sends static shell now] B --> C[Browser paints shell, fast TTFB] D[Slow async component resolves] --> E[Server streams the chunk] E --> F[Suspense fallback swaps to real content]