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

Islands ข้าม Frameworks

ขอบเขต micro-frontend ที่ถูกที่สุดที่คุณจะเคยวาดได้คือ Astro island island คือ component แบบโต้ตอบที่เบ็ดเสร็จในตัวเอง ที่ hydrate ด้วยตัวเอง ส่ง JavaScript ของตัวเอง และไม่รู้อะไรเลยเกี่ยวกับ component รอบข้าง เปลี่ยน “component” เป็น “team” แล้วคุณก็ได้ micro-frontend: region ที่แยกตัวออกมาซึ่งกลุ่มหนึ่งเป็นเจ้าของ และ shell เพียงแค่วางลงบนหน้าเพจ

ส่วนที่น่าทึ่งคือ Astro ปล่อยให้ islands มาจาก framework ต่างกันบนหน้าเพจเดียวกัน ได้ ทีม Catalog สามารถเขียน Vue ทีม Reviews สามารถเขียน Svelte และทีม Cart สามารถอยู่กับ React ต่อไป — ทั้งหมดถูกเรนเดอร์ลงในเอกสาร HTML แบบ static เดียวกัน

แต่ละ framework ที่คุณต้องการ host อยู่ห่างจาก astro add เพียงคำสั่งเดียว คำสั่งนี้จะติดตั้ง renderer และจัดการ config ให้คุณ:

Terminal window
npx astro add react vue svelte

หลังจากนั้น การ import component .jsx, .vue, หรือ .svelte เข้าไปในไฟล์ .astro ก็ทำงานได้เลย คุณเลือกให้แต่ละตัวเข้าสู่ hydration ด้วย client:* directive — client:load, client:idle, หรือ client:visible — และ Astro จะส่งเฉพาะ runtime ของ island นั้น เฉพาะตอนที่จำเป็นเท่านั้น

flowchart TD
  Page["Astro page (static HTML shell)"]
  Page --> RI["React island — Cart\n(client:load)"]
  Page --> VI["Vue island — Catalog\n(client:visible)"]
  Page --> SI["Svelte island — Reviews\n(client:idle)"]
  RI -.->|isolated| VI
  VI -.->|isolated| SI
หนึ่งหน้าเพจ, สาม framework islands, สามขอบเขตที่เป็นอิสระต่อกัน

แต่ละ island คือขอบเขตของตัวเอง การ crash ภายใน Vue island ไม่ทำให้ React island ล้มตามไปด้วย แต่ละตัว hydrate อย่างเป็นอิสระและเป็นเจ้าของเฉพาะ subtree ของตัวเองเท่านั้น

ใน React SPA ทุก region ที่โต้ตอบได้อยู่ภายใน component tree เดียวและ bundle เดียว ใน Astro นั้น หน้าเพจ คือ shell และแต่ละ region คือ island ที่ shell วางลงไป — โดยอาจมาจาก framework ที่ต่างกัน

React
// React SPA: one tree, one bundle.
// Every region is a React component,
// all hydrated together at startup.
export default function StorePage() {
return (
<Shell>
<Cart /> {/* React */}
<Catalog /> {/* React */}
<Reviews /> {/* React */}
</Shell>
);
}
Astro
---
// Astro page: the shell is static HTML.
// Each region is an island — and each may
// be a different framework owned by a
// different team.
import Cart from '../islands/Cart.jsx'; // React
import Catalog from '../islands/Catalog.vue'; // Vue
import Reviews from '../islands/Reviews.svelte'; // Svelte
---
<main>
<Cart client:load />
<Catalog client:visible />
<Reviews client:idle />
</main>

playground ด้านล่างเป็นหน้า Astro ที่สมบูรณ์และรันได้จริง หน้านี้ host React counter และ Vue counter ในเอกสารเดียวกัน — สอง framework, สอง islands ที่เป็นอิสระต่อกัน, หนึ่ง static shell เปิดใน StackBlitz แล้วดูแต่ละ island hydrate และเก็บ state ของตัวเอง

Astro
---
// src/pages/index.astro
// Each island below could be owned by a
// different team. The page is the shell.
import ReactCounter from '../components/ReactCounter.jsx';
import VueCounter from '../components/VueCounter.vue';
---
<html lang="en">
  <head><title>MFE islands demo</title></head>
  <body>
    <h1>One page, two frameworks</h1>

    <section>
      <h2>Cart team (React)</h2>
      <ReactCounter client:load />
    </section>

    <section>
      <h2>Catalog team (Vue)</h2>
      <VueCounter client:visible />
    </section>

    <p>Each counter keeps its own state, isolated.</p>
  </body>
</html>

หากต้องการรันจริงๆ คุณจะต้อง npx astro add react vue จากนั้นเพิ่ม src/components/ReactCounter.jsx (React component ปกติที่ใช้ useState) และ src/components/VueCounter.vue (Vue component ปกติที่ใช้ ref) component ทั้งสองไม่รับรู้ถึงกันและกัน — การแยกตัวนั้นแหละคือประเด็นทั้งหมด

islands คือทางเข้าสู่ micro-frontends ที่นุ่มนวลที่สุด: ไม่มี infrastructure เพิ่มเติม, ไม่มี remote loading, ไม่มี Module Federation — เป็นเพียง component จากทีม (และ framework) ต่างๆ ที่ประกอบกันโดย Astro shell ที่รวดเร็ว บทเรียนถัดไปจะขยายเรื่องนี้ขึ้นไปสู่ระดับ package ทั้งก้อนและ server fragment ที่ถูกเลื่อน (deferred)

คำสั่งใดเพิ่ม renderer ของ React, Vue และ Svelte เข้าไปในโปรเจกต์ Astro?
ทำไม Astro islands จึงเป็นขอบเขต micro-frontend ตามธรรมชาติ?
`client:*` directive ควบคุมอะไรสำหรับ island?