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

Island คืออะไร?

ในแอป React หรือ Next.js browser ได้รับ JavaScript bundle ขนาดใหญ่หนึ่งก้อน React บูต เดิน component tree ทั้งหมด และแนบ event listeners ทุกที่ — แม้แต่กับ component ที่ไม่มีการโต้ตอบใดๆ นี่คือ full-app hydration

Island Architecture ที่คิดค้นโดย Jason Miller และทำให้เป็นที่นิยมโดย Astro พลิกค่าเริ่มต้น: เพจเป็น HTML ที่ไม่ทำงาน มีเฉพาะ component ที่ต้องการ interactivity เท่านั้นที่ถูก hydrate และ hydrate อย่างอิสระ — เหมือนเกาะในทะเลของ markup แบบ static

React
// Next.js page — the WHOLE tree hydrates.
// Even <Header> and <Footer> download JS.
export default function BlogPost({ post }) {
return (
<>
<Header /> {/* JS downloaded + hydrated */}
<article>{post.content}</article> {/* JS downloaded + hydrated */}
<LikeButton postId={post.id} /> {/* JS downloaded + hydrated */}
<RelatedPosts posts={post.related} />{/* JS downloaded + hydrated */}
<Footer /> {/* JS downloaded + hydrated */}
</>
);
}
// Total JS budget: EVERYTHING
Astro
---
// Astro page — only <LikeButton> needs JS.
import Header from '../components/Header.astro';
import LikeButton from '../components/LikeButton.jsx';
import RelatedPosts from '../components/RelatedPosts.astro';
import Footer from '../components/Footer.astro';
const { post } = Astro.props;
---
<Header /> <!-- 0 KB JS -->
<article>{post.content}</article> <!-- 0 KB JS -->
<LikeButton client:visible postId={post.id} /> <!-- hydrated -->
<RelatedPosts posts={post.related} /> <!-- 0 KB JS -->
<Footer /> <!-- 0 KB JS -->
{/* Total JS budget: ONE component */}

ในหน้าเพจที่มีเนื้อหาเป็นหลัก เช่น บล็อก, landing page, หรือเว็บ docs — ส่วนใหญ่ของ component เป็นแค่การแสดงผล ไม่ตอบสนองต่อการคลิก การ hydrate เหล่านั้นสิ้นเปลือง CPU และทำให้ Time to Interactive ช้าลง

Astro วัดสิ่งนี้อย่างแม่นยำ เพจที่มี island LikeButton เดียวจะดาวน์โหลดเฉพาะ JavaScript สำหรับปุ่มนั้นและ React runtime ของตัวเอง — ไม่มีอื่นนอกจากนั้น Next.js เทียบเท่าส่ง React บวก component ทุกตัวบนเพจ

ผลลัพธ์จริงในเว็บที่มีเนื้อหาหนาแน่น:

  • ลด JavaScript ที่ส่งได้ 90%+
  • Largest Contentful Paint เร็วขึ้น — HTML มาถึงพร้อม parse แล้ว ไม่มี hydration blocking การ render
  • Time to Interactive ต่ำลง — มีเพียง island เดียวที่ต้องบูท

รูปแบบนี้เรียกว่า partial hydration เพราะมีเพียง บางส่วน ของเพจที่ถูก hydrate แต่ละ island:

  1. Render เป็น HTML บน server/build (เหมือน .astro component ทั่วไป)
  2. ส่ง JS bundle ของตัวเองไปยัง browser
  3. Hydrate อย่างอิสระ — islands ไม่แชร์ React root

จุดสุดท้ายสำคัญมาก: islands ถูกแยกออกจากกัน ไม่มี React tree เดียวที่เชื่อมต่อกัน ซึ่งหมายความว่าไม่มีการแชร์ React context ข้าม islands โดยค่าเริ่มต้น คุณจะได้เรียนรู้วิธีจัดการกับนั้นใน เมื่อใดควรใช้ Island

React
// React: ONE root, one reconciler, shared context
import ReactDOM from 'react-dom/client';
ReactDOM.createRoot(document.getElementById('root'))
.render(<App />);
// Every component inside <App> is part of this tree.
// React.createContext() works across the whole tree.
Astro
---
// Astro: MULTIPLE roots, one per island
// Astro mounts each island independently:
// ReactDOM.createRoot(islandEl).render(<Counter />)
// ReactDOM.createRoot(likeEl).render(<LikeButton />)
// Each island has its own React root.
// React.createContext() does NOT cross island boundaries.
import Counter from '../components/Counter.jsx';
import LikeButton from '../components/LikeButton.jsx';
---
<Counter client:load />
<LikeButton client:visible postId={42} />
'Partial hydration' คืออะไร?
ใน Astro เพจที่มี React islands สามอันจะสร้าง React roots กี่อัน?
ทำไม full-app hydration จึงเพิ่ม Time to Interactive แม้ในเพจที่เป็น static เป็นส่วนใหญ่?
React Context ทำงานข้าม Astro islands สองอันบนเพจเดียวกันหรือไม่?