Haliviq
Next.js

Server Components render ใหม่ทุกครั้งที่เปลี่ยนหน้า ไม่ใช่แค่ตอน mount

เราเคยเข้าใจว่าผลลัพธ์ของ Server Component ถูก cache ข้ามหน้า แต่จริง ๆ Next.js ดึงใหม่ทุกครั้งที่กดลิงก์เปลี่ยนหน้า เว้นแต่เราตั้งค่า caching เอง ถ้าหน้าไหนช้าหลังกดลิงก์ ให้ดูว่า server component ดึงอะไรอยู่ แล้วตัดสินใจให้ชัดว่าอะไรควร cache และนานแค่ไหน

เกิดอะไรขึ้น

เราเคยเข้าใจว่าพอ Server Component render เสร็จแล้ว ผลลัพธ์จะถูกใช้ซ้ำเมื่อผู้ใช้เปลี่ยนหน้าไปแล้วกลับมา เหมือนถูก cache ไว้ แต่พอกดลิงก์ในหน้าเว็บ บางหน้ากลับช้า และ log ฝั่ง server แสดงว่าการดึงข้อมูลรันใหม่ทุกครั้งที่เข้าหน้านั้น

ตอนแรกเราไล่หาบั๊กใน client ทั้งที่ต้นเหตุอยู่ที่ฝั่ง server

ทำไมถึงเป็นแบบนั้น

ผลลัพธ์ของ Server Component ไม่ใช่ cache ในตัวมันเอง เวลา soft navigation ฝั่ง client จะขอ RSC payload ของ route segment ใหม่จาก server และ server ก็ render ใหม่ layout ที่ใช้ร่วมกันระหว่างหน้าเก่ากับหน้าใหม่จะไม่ถูก render ซ้ำ แต่ segment ของ page จะถูก render

ส่วนที่ใช้ซ้ำได้ขึ้นกับเวอร์ชันและการตั้งค่า ใน Next.js 14 Router Cache ฝั่ง client เก็บหน้า dynamic ที่เคยเข้าไว้ 30 วินาที และ Data Cache ของ fetch() เปิดเป็นค่าเริ่มต้น ส่วน Next.js 15 เปลี่ยนค่าเริ่มต้น คือ fetch ไม่ถูก cache และ segment ของ page จะไม่ถูกใช้ซ้ำจาก Router Cache เว้นแต่ตั้ง staleTimes ดังนั้นอย่าสมมติพฤติกรรมเอาเอง

ตอนนี้เราทำอย่างไร

ตัดสินใจเป็นราย fetch ให้ชัด ข้อมูลที่เปลี่ยนไม่บ่อยให้ cache ไว้พร้อม tag แล้ว revalidate ด้วย revalidateTag ตอนข้อมูลเปลี่ยน ข้อมูลเฉพาะผู้ใช้ให้เป็น dynamic ต่อไป และถ้าหลาย component ใน render เดียวกันอ่านข้อมูลเดียวกัน ให้ใช้ React cache() ช่วยไม่ให้ดึงซ้ำ

เวลาทดสอบให้กดลิงก์เปลี่ยนหน้าจริง ๆ ไม่ใช่แค่ hard refresh เพราะสองแบบนี้เดินคนละเส้นทางของ cache

cache ไว้จนกว่าจะเรียก revalidateTag('products')ts
const res = await fetch('https://api.example.com/products', {
  cache: 'force-cache',
  next: { tags: ['products'] },
})
const products = await res.json()

เช็กลิสต์สั้น ๆ

  1. 1เปิดหน้าที่ช้า แล้ว log ว่าแต่ละ Server Component ดึงอะไรบ้าง
  2. 2เช็กเวอร์ชัน Next.js เพราะค่าเริ่มต้นของ 14 กับ 15 ต่างกัน
  3. 3ตัดสินใจรายตัวว่า fetch ไหนต้องสดเสมอ, cache จนกว่าจะ revalidate tag หรือ cache เป็นช่วงเวลา
  4. 4ใช้ React cache() ลดการอ่านข้อมูลซ้ำใน render เดียวกัน
  5. 5ทดสอบด้วยการคลิกลิงก์ ไม่ใช่แค่ reload

จำไว้สั้น ๆ

อย่าสมมติว่าผลลัพธ์ของ Server Component ถูก cache ให้ตัดสินใจเรื่อง caching ของแต่ละ fetch ให้ชัดเจน

เราอยากฟังว่าคุณกำลังสร้างอะไรอยู่

คุยกับเรา