上一講我們聊了 Astro 和島嶼架構,將需要互動的區塊拆成一座座獨立的區塊,使得前端能載入最少的 JavaScript 達成必要的互動,讓網站的效能變得更佳。
從這講開始,我們便會開始進入實作的篇章,開始用 Astro 來打造一個部落格吧!
在這個專案開始之初,我們會安裝及使用以下基本 Library:
.astro
的模板,若是使用 VSCode,可以 IDE 中安裝 Astro Extension 幫助解析模板檔案初始化的步驟包含 npm init
、npm install astro
及相關 Libraries,用 git init
初始化並加上 .gitignore
(包含 node_modules/
)。
接著在 package.json
中準備第一份 Astro Scripts開發時執行 astro dev
,建置和預覽建置後的成果則使用 astro build
和 astro preview
。
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
}
再來就可以撰寫第一個 Hello World 的頁面
public/favicon.svg
- 先在專案目錄下建立 public/
資料夾準備一個瀏覽器會顯示的 Iconsrc/pages/index.astro
- 建立 src/
作為所有程式碼的入口、pages
則是 Astro 做路由的依據,首頁會以這個 index.astro
檔案為入口---
console.log('Terminal testing');
---
<html>
<head>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
<body>
<h1>Hello, World</h1>
</body>
<style>
h1 {
color: orange;
}
</style>
</html>
src/pages/index.astro
上面是一個簡單的 Hello World 頁面,首先最上方用 ---
包覆起來的區塊稱作 frontmatter,可以在 .astro
檔案中寫 JavaScript 或 TypeScript 的程式碼,我們塞入 console.log
來測試能否在 Astro 的 Terminal 中印出 Terminal testing
。
最後便能夠執行 npm run dev
來啟動 Astro Dev Server,透過瀏覽器驗證 Logo 是否有載入成功、將標題改成橘色的樣式能否順利渲染,並在 Terminal 查看是否有 Terminal testing
印出。
*成功渲染 Hello, World
若要使用 TypeScript,因為安裝 Astro 時就已經預設支援,我們只需要新增以下這段 Snippet 到 tsconfig.json
即可,用以指定 Astro 官方建議的 TypeScript 基本設定。
{
"extends": "astro/tsconfigs/base"
}
而我平常使用 React 來做前端開發,因此參考 @astro-react,透過 npm install @astrojs/react react react-dom && npm install -D @types/react @types/react-dom
來安裝這些 Dependencies。
接著再新增 astro.config.ts
,裡面貼上
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react()],
});
為了測試 React 的 Component 是否能執行,我們可以在 src/
下新增 components/Counter.tsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState<number>(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
並且修改 index.astro
,加上
---
import Counter from '../components/Counter';
---
<h1>Hello, World</h1>
<Counter client:load />
*React Button Component
測試點擊 Increment / Decrement 來驗證 Count 是否有變化。
需要注意的是,Astro 預設會將所有 UI component 轉換成 HTML 及 CSS,將 Client-side JavaScript 盡可能移除,藉以使得載入渲染速度更快。
而當我們要加入能夠互動的 React Component 時,要記得加上 client:load
directive 來將這個 Component 打包成 Client Island,這個獨立出來的 Island 才能包含互動的行為。
由於要架設一個部落格,核心是以內容為本,我們需要專注在內容的撰寫之上。如果在寫文章的同時還要重新寫一整段 HTML、調整部分內容的 CSS,就算是把舊的複製貼上,一樣會很沒有效率。
因此,Astro 理所當然的有提供稱作 Astro Components 及 Astro Layouts 的基礎元件,讓我們做完版面的定義後,就可以專心在內容的產出了。
提到 Layouts 之前,我們得先知道什麼是 Astro Components,這是一種副檔名為 .astro
,能讓我們撰寫能夠重複利用的 UI Block。
每個 Component 包含 Frontmatter(也就是 ---
所包覆的區塊)及後續的 HTML 內容。其行為很像 React 的 Functional Components,都可以定義從外部傳入的參數,這在 Astro 中稱作 Component Props。
例如在 src/components
底下建立一個稱作 Greeting.astro
的 Astro Component:
---
type Props = { name: string };
const { name } = Astro.props;
---
<p>Hello {name}</p>
定義了可傳入的 Prop 變數 name
,而這個 Component 則很簡單的顯示 Hello 加上傳入的變數。
在其他頁面如果要使用這個 Component,就只需要引入、傳入參數,例如:只要需要在 index.astro
中加入 import
---
import Greeting from '../components/Greeting.astro';
---
再來直接使用元件並傳入參數
<Greeting name="John" />
就能夠印出 Hello John
的文字在瀏覽器中,是不是和 React 非常像?這是因為 Astro 的模板也是受到 JSX 的格式所啟發的。
而 Astro Layouts 則是一種特殊的 Astro Components,主要是為了定義頁面的結構,例如說,一個 Page 的模板。
基本的 Astro Layouts 通常包含完整的 HTML 頁面元素如 <html>
、<head>
、<body>
等等標籤。當撰寫完 Layout 後,可以讓 .astro
、.md
等頁面檔案使用,這些頁面中的子內容就會被插入在 Layout 當中有寫 <slot />
的部分。
舉例來說,我們可以定義一個文章的模板在 src/layouts/BlogPostLayout.astro
:
---
const { frontmatter } = Astro.props;
---
<html>
<head>
<meta charset="UTF-8" />
<title>{frontmatter.title}</title>
</head>
<body>
<article>
<h1>{frontmatter.title}</h1>
<p>發佈時間:{frontmatter.publishedTime}</p>
<p>最後更新:{frontmatter.updatedTime}</p>
<slot />
</article>
</body>
</html>
便可以套用此模板來撰寫一個 .md
的文章(src/pages/posts/first-post.md
):
---
layout: ../../layouts/BlogPostLayout.astro // Use the layout
title: '第一篇部落格貼文'
publishedTime: '2025-05-07 23:27:00'
updatedTime: '2025-05-07 23:27:00'
---
# The First Blog Post
測試內容
Astro 的編譯機制會自動抓取 Markdown frontmatter 下的 layout 路徑,並將整篇文章的內容替換掉我們定義的 元素。
產出的結果如下:
*部落格頁面範例
一般來說部落格的文章用 Markdown 寫其實就相當足夠了,但若是想使用 .astro
的格式來更彈性的插入一些元素,也可以這麼做。
新增 src/pages/posts/second-post.astro
):
---
import BlogPostLayout from '../../layouts/BlogPostLayout.astro';
const frontmatter = {
title: '第二篇部落格貼文',
publishedTime: '2025-05-07 23:27:00',
updatedTime: '2025-05-07 23:27:00',
};
---
<BlogPostLayout frontmatter={frontmatter}>
<h1>第二篇貼文</h1>
<p>測試內容</p>
</BlogPostLayout>
如此一來,就能在原生的 .astro
檔案中也套用 Layout。