元件
Astro 元件是 Astro 專案的基石,只包含 HTML 模板,不依賴客戶端運行環境。你可以從副檔名辨別它:.astro
Astro 元件極其彈性,能包含共用 UI(像是頁首、個人資訊卡)、一小段 HTML(例如跟 SEO 有關的 <meta>
標籤),甚至整個網頁版面。
Astro 元件最重要的一點是:不在客戶端渲染。它們只在建置階段或伺服器端渲染(SSR)需要時轉換為 HTML。元件的 frontmatter 可放入 JavaScript 程式碼,它們不會送至使用者的瀏覽器。預設不包含任何 JavaScript 足跡,因此你的網站變得更快了。
如果需要客戶端的互動效果,可適時加入 HTML <script>
標籤或 UI 框架元件。
元件架構
標題為 元件架構Astro 元件由兩塊組成:元件腳本和元件模板。兩者各司其職,攜手構築出既容易使用,又能滿足多樣開發需求的框架。
---// 元件腳本(JavaScript)---<!-- 元件模板(HTML + JS 表達式) -->
元件腳本
標題為 元件腳本Astro 使用「程式碼圍欄」(---
)區別元件腳本。如果你曾寫過 Markdown,你可能知道 frontmatter,兩者是相似的概念。Astro 元件腳本的構想就是受此概念啟發。
你可在元件腳本中撰寫渲染模板時需要用到的 JavaScript 程式碼。包含:
- 匯入其他 Astro 元件
- 匯入其他框架元件,例如 React
- 匯入資料,例如 JSON 檔
- 從 API 或資料庫抓取資料
- 建立變數供模板使用
---import SomeAstroComponent from '../components/SomeAstroComponent.astro';import SomeReactComponent from '../components/SomeReactComponent.jsx';import someData from '../data/pokemon.json';
// 存取傳入元件的參數,例如 `<X title="Hello, World" />`const { title } = Astro.props;// 抓取外部資料,從非公開 API 或資料庫都可以const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());---<!-- 模板在這! -->
程式碼圍欄的設計理念是要「圍住」你寫的 JavaScript,確保它不會跑到前端應用程式、落入使用者手中。在圍欄裡可以安心地寫較耗資源或者敏感的程式碼(例如呼叫你的私有資料庫),不必擔心裡面的程式碼不小心出現在使用者的瀏覽器裡。
元件腳本裡甚至可以寫 TypeScript!
元件模板
標題為 元件模板元件模板位於程式碼圍欄下方,它決定了最終產出的 HTML。
寫在這裡的 HTML 會被元件渲染,當元件被其他 Astro 頁面匯入使用時亦同。
不只一般的 HTML,Astro 的元件模板語法 也支援 JavaScript 表達式、Astro <style>
和 <script>
標籤、匯入的元件,以及特殊的 Astro 指令。
---// 這裡是元件腳本!import Banner from '../components/Banner.astro';import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';const myFavoritePokemon = [/* ... */];const { title } = Astro.props;---<!-- 支援 HTML 註解! -->{/* JS 註解也沒問題! */}
<Banner /><h1>Hello, world!</h1>
<!-- 使用 props 和元件腳本的變數: --><p>{title}</p>
<!-- 透過 `client:` 指令引入其他 UI 框架元件:--><ReactPokemonComponent client:visible />
<!-- 在 HTML 使用 JavaScript 表達式,類似 JSX:--><ul> {myFavoritePokemon.map((data) => <li>{data.name}</li>)}</ul>
<!-- 使用模板指令合併 class name,字串甚至是物件都可以! --><p class:list={["add", "dynamic", {classNames: true}]} />
以元件為基礎的設計
標題為 以元件為基礎的設計元件的設計理念是要能夠重複使用和組合。你可以在元件裡使用其他元件,創造出更多更複雜的 UI。舉例來說,Button
元件可以組合成 ButtonGroup
元件:
---import Button from './Button.astro';---<div> <Button title="Button 1" /> <Button title="Button 2" /> <Button title="Button 3" /></div>
元件參數
標題為 元件參數Astro 元件可以自訂和接受參數,供元件模板渲染 HTML 時使用。參數可以在 frontmatter 腳本透過全域的 Astro.props
存取。
以下範例中,元件接受傳入的 greeting
和 name
參數。注意參數是從全域的 Astro.props
物件解構出來的。
---// 用法:<GreetingHeadline greeting="Howdy" name="Partner" />const { greeting, name } = Astro.props;---<h2>{greeting}, {name}!</h2>
其他 Astro 元件/版面/頁面,可以傳遞參數給 GreetingCard
元件:
---import GreetingHeadline from './GreetingHeadline.astro';const name = 'Astro';---<h1>歡迎卡片</h1><GreetingHeadline greeting="Hi" name={name} /><p>祝你有個美好的一天!</p>
你可以用 TypeScript 自訂 Props
的型別,Astro 能自動偵測 frontmatter 的 Props
,依此顯示警告/錯誤訊息。另外,從 Astro.props
解構參數時,能夠設定預設值。
---interface Props { name: string; greeting?: string;}
const { greeting = "Hello", name } = Astro.props;---<h2>{greeting}, {name}!</h2>
未傳入參數值時,可以設定預設值。
---const { greeting = "Hello", name = "Astronaut" } = Astro.props;---<h2>{greeting}, {name}!</h2>
插槽
標題為 插槽<slot />
元素是為外部 HTML 內容預留的位置,供子元素從其他檔案放入元件模板用。
預設情況下,元件會渲染所有透過 <slot />
傳入的子元素。
參數 是傳遞給 Astro 元件的屬性,可以在元件中透過 Astro.props
存取,而 插槽 則是在它們被寫下的位置渲染 HTML 子元素。
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot /> <!-- 子元素會跑到這 --> <Footer /></div>
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <h2>關於 Fred</h2> <p>關於 Fred 的一些資訊。</p></Wrapper>
一整頁 HTML 內容被 <SomeLayoutComponent></SomeLayoutComponent>
標籤「包起來」,接著傳給其他元件渲染頁面所有元素——這個模式便是 Astro 版面元件的基礎。
具名插槽
標題為 具名插槽Astro 元件可以放置具名插槽,讓你只傳入名字相符的 HTML 元素到指定插槽。
具名插槽使用 name
屬性:
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <slot name="after-header" /> <!-- 有 `slot="after-header"` 屬性的子元素會跑來這 --> <Logo /> <h1>{title}</h1> <slot /> <!-- 沒有 `slot` 屬性的子元素,以及有 `slot="default"` 屬性的子元素會跑來這 --> <Footer /> <slot name="after-footer" /> <!-- 有 `slot="after-footer"` 屬性的子元素會跑來這 --></div>
要把 HTML 內容放進特定的插槽,必須在子元素加上 slot
屬性指定對應的插槽,所有其他子元素則會被放入預設(不具名)的 <slot />
。
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <img src="https://my.photo/fred.jpg" slot="after-header" /> <h2>關於 Fred</h2> <p>關於 Fred 的一些資訊。</p> <p slot="after-footer">版權所有 2022</p></Wrapper>
在子元素加上 slot="my-slot"
,藉此將它們傳遞到對應的 <slot name="my-slot" />
預留位置。
若要一次傳遞多個 HTML 元素,但不想額外包一層 <div>
的話,可以在 Astro 的 <Fragment/>
元件使用 slot=""
:
---// 客製一個 table,使用具名插槽為 head 和 body 預留兩個位置---<table class="bg-white"> <thead class="sticky top-0 bg-white"><slot name="header" /></thead> <tbody class="[&_tr:nth-child(odd)]:bg-gray-100"><slot name="body" /></tbody></table>
透過 slot=""
屬性,將表格內容放置於 "header"
和 "body"
。還能單獨調整 HTML 元素的樣式。
---import CustomTable from './CustomTable.astro';---<CustomTable> <Fragment slot="header"> <!-- 表首會傳到這 --> <tr><th>產品名稱</th><th>庫存量</th></tr> </Fragment>
<Fragment slot="body"> <!-- 內容會傳到這 --> <tr><td>夾腳拖</td><td>64</td></tr> <tr><td>靴子</td><td>32</td></tr> <tr><td>球鞋</td><td class="text-red-500">0</td></tr> </Fragment></CustomTable>
只能透過具名插槽傳送一層子元素,不能傳送巢狀元素,請特別留意。
具名插槽也可傳遞到 UI 框架元件!
Astro 插槽名稱無法動態產生(例如透過 map 函式操作)。想在 UI 框架元件裡動態產生插槽名稱的話,最好還是讓框架本身去處理。
插槽的備用內容
標題為 插槽的備用內容插槽也能渲染備用內容。沒有對應的子元素傳遞過來時,<slot />
元素會渲染自己的備用內容。
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot> <p>這是我的備用內容,如果沒有傳子元素到 slot 時會顯示。</p> </slot> <Footer /></div>
轉移插槽
標題為 轉移插槽插槽可以轉移到其他元件。以建立巢狀版面為例:
------<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <slot name="head" /> </head> <body> <slot /> </body></html>
---import BaseLayout from './BaseLayout.astro';---<BaseLayout> <slot name="head" slot="head" /> <slot /></BaseLayout>
name
和 slot
屬性都可將具名插槽轉移至其他元件。
傳給 HomeLayout
的預設插槽和 head
插槽,現在會被轉移到上層的 BaseLayout
。
---import HomeLayout from '../layouts/HomeLayout.astro';---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout>
HTML 元件
標題為 HTML 元件Astro 支援將 .html
檔案匯入成元件,或放置到 src/pages
子目錄裡當作頁面。如果你想在不使用框架的前提下重複利用現有網站程式碼,或想確保元件沒有動態功能的話,可以考慮使用 HTML 元件。
HTML 元件只能包含合法的 HTML,沒有 Astro 元件的特色功能:
- 不支援 frontmatter,伺服器端匯入,以及動態表達式
<script>
標籤不會被打包,行為同 Astro 的is:inline
屬性- 只能連結
public/
資料夾裡的靜態資源
HTML 元件裡的 <slot />
元素可以正常運作。如果想用 HTML Web Component 插槽 元素的話,在 <slot>
元素加上 is:inline
。