iT邦幫忙

2025 iThome 鐵人賽

DAY 12
1
Modern Web

手刻部落格,從設計到部署的實戰攻略系列 第 12

Astro(二):初始化專案,理解 Astro Components 及 Layouts 基礎

  • 分享至 

  • xImage
  •  

上一講我們聊了 Astro 和島嶼架構,將需要互動的區塊拆成一座座獨立的區塊,使得前端能載入最少的 JavaScript 達成必要的互動,讓網站的效能變得更佳。

從這講開始,我們便會開始進入實作的篇章,開始用 Astro 來打造一個部落格吧!

Hello World, Astro

在這個專案開始之初,我們會安裝及使用以下基本 Library:

  • 執行的環境使用 Node.js、套件管理用最基本的 NPM、版本管理 git
  • 框架類則使用這個篇章的主角:Astro,並使用 TypeScript 作為程式語言、React 來處理前端互動邏輯
  • 輔助性的 Library 會用到 PrettierESLint 做程式碼格式化、找出潛在錯誤
  • 由於 Astro 有自己副檔名為 .astro 的模板,若是使用 VSCode,可以 IDE 中安裝 Astro Extension 幫助解析模板檔案

初始化的步驟包含 npm initnpm install astro 及相關 Libraries,用 git init 初始化並加上 .gitignore(包含 node_modules/)。

接著在 package.json 中準備第一份 Astro Scripts開發時執行 astro dev,建置和預覽建置後的成果則使用 astro buildastro preview

"scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview"
}

再來就可以撰寫第一個 Hello World 的頁面

  • public/favicon.svg - 先在專案目錄下建立 public/ 資料夾準備一個瀏覽器會顯示的 Icon
  • src/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

*成功渲染 Hello, World

加入 TypeScript 及 React

若要使用 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

*React Button Component

測試點擊 Increment / Decrement 來驗證 Count 是否有變化。

需要注意的是,Astro 預設會將所有 UI component 轉換成 HTML 及 CSS,將 Client-side JavaScript 盡可能移除,藉以使得載入渲染速度更快。

而當我們要加入能夠互動的 React Component 時,要記得加上 client:load directive 來將這個 Component 打包成 Client Island,這個獨立出來的 Island 才能包含互動的行為。

Astro Components

由於要架設一個部落格,核心是以內容為本,我們需要專注在內容的撰寫之上。如果在寫文章的同時還要重新寫一整段 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 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。

參考資料

  1. Astro - Manual Setup
  2. Astro - Components
  3. Astro - Layouts

上一篇
Astro(一):輕又快的靜態網站產生器,淺談島嶼架構
下一篇
Astro(三):用 MDX 寫文章,顯示程式碼、數學式,搭配 JavaScript 服用
系列文
手刻部落格,從設計到部署的實戰攻略19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言