打造高分作品集網站超簡單:Astro 框架基礎介紹

想要開發作品集或部落格等靜態網站?來試試以內容為中心、SEO 友善且高效能的超人氣新框架 Astro 吧

Kelly CHI
22 min readJan 14, 2024
Start building with Astro today

目錄

· 什麼是 Astro?
· Island Architecture 島嶼架構
· View Transition API
· 開始開發吧!
· 結語

前言

在競爭激烈的 JavaScript 生態系中,各式各樣的框架如雨後春筍般不斷冒出,React、Vue 等「經典」框架已經不夠看了,更現代化的框架如 Svelte、Solid、Qwik 等等也受到越來越多開發者的青睞。其中,一個相當值得關注的框架就是我們今天要談的 Astro。從 NPM 的下載紀錄可以看到,Astro 近年可說是獲得了爆炸性的成長,它的高效能和友善的開發者體驗,讓它在前後端開發社群中備受矚目。

Astro 推出後的兩年內,npm 下載次數持續成長

我之所以會接觸到 Astro 這個框架,也是因為看到開發者社群的討論以及 Youtube 上的大神推薦,並且也剛好也有製作作品集網站的需求,就趁著這個機會來好好的認識一下這個新框架!

Snowpack、Astro 開發者 Fred K. Schott
Snowpack、Astro 開發者 Fred K. Schott

什麼是 Astro?

以內容為中心

根據官方文件,Astro 將自己定位成一個旨在建立高效能、「以內容為中心 (content-focused)」的 JavaScript 框架。所謂「以內容為中心」的網頁,指的是像是部落格、行銷網站、接案公司形象網,或是電子商務平台等這種以內容展示為主、互動性較低的網頁。針對這些網頁,Astro 提供了許多常見功能的支援,例如多語系網站(i18n)、網站地圖、內容管理系統(CMS)和 Markdown/MDX 語法的原生支援。

所謂「以內容為中心」的網頁,指的是像是部落格、行銷網站、接案公司形象網,或是電子商務平台等這種以內容展示為主、互動性較低的網頁

0 JavaScript by Default

在效能方面,Astro 將所謂的「島嶼架構 (Island Architecture)」發揚光大,並預設不會載入任何的 JavaScript,讓使用者可以在網頁載入後立即與網頁互動,而不用去經歷「Uncanny Valley」這個過程 (意即雖然網頁看起來是準備好的,但實際上卻無法與之互動)。也因為不需要載入任何 JavaScript,Astro 與其他 SSR 框架相比之下,效能表現更突出。有興趣進一步閱讀的人可以參考這篇官方文章:2023 Web Framework Performance Report

Astro 可以帶來超級高的 Performance

用任何你喜歡的框架

Astro 另外一個很大的賣點是,它是 「框架不可知 (framework agnostic)」 的。不像用 Next.js 需要用 React,用 Nuxt.js 就要用 Vue,Astro 並沒有和任何框架綁定,我們可以依照喜好和需求自由地使用任何前端框架,或是乾脆不要使用任何的框架。這讓原本並非前端專業的開發者,或是正在學習開發的新手可以享受它帶來的好處,卻減少學習新前端框架的知識負擔。

使用插件可以輕鬆將其他前端框架加入到 Astro 專案中
使用插件可以輕鬆將其他前端框架加入到 Astro 專案中

Island Architecture 島嶼架構

這邊要稍微介紹一下 Astro 的主要特色之一,「島嶼架構」。

「島嶼架構」其實並非 Astro 創造出來的概念,早在幾年前就已經有相關的討論和框架實現。之所以會被稱為「島嶼」,是因為我們可以把網頁中各個元件或是區域視為一個個獨立的「島嶼」,這些「島嶼」會各自處理內部的腳本載入且不會互相影響,就像是獨立的應用程式。

如下圖所示,一個網頁中可能包含了靜態的圖片、廣告,也包含了動態的側邊欄和圖片輪播,在「島嶼架構」之下,不需要與使用者進行互動的靜態內容區塊就不會經過 Hydration 的過程,我們就可以減少載入的 JavaScript 總量,提升網站的效能。動態區域的腳本也可以透過非同步的載入,讓網站更快能與使用者互動。

一個網頁中可能包含了靜態的圖片、廣告,也包含了動態的側邊欄和圖片輪播,在「島嶼架構」之下,不需要與使用者進行互動的靜態內容區塊就不會經過 Hydration 的過程,我們就可以減少載入的 JavaScript 總量,提升網站的效能
「島嶼架構」示意圖

簡單來說,「島嶼架構」旨在尋求「高互動性的 CSR (Client-side Rendering) 應用程式」和「具有高度 SEO 表現的 SSG (Static Site Generation) 靜態網頁」之間的平衡,同時也解決 SSR (server-side rendering) 框架 Full Hydration 所帶來的問題。

Astro 所主打的「以內容為中心」,就十分適合採取這個策略,因為大部分的部落格、電商網站中的內容都是靜態的,但我們也會需要像是切換深淺色、購物車等簡單的互動。島嶼架構可以讓這些網站保持高 SEO,也能帶給使用者舒適的互動體驗。

總之,這些渲染模式和他們所帶來的效能影響並非三言兩語可以說完的,想要閱讀更多關於島嶼架構的討論,可以參考 Awesome Islands GitHub 這個 Repo,裡面集結了各種關於島嶼架構的文章和影片資源。若是對其它的渲染模式有興趣的話,也可以收藏 Patterns.dev 這個網頁。

View Transition API

Astro 另外一個值得一提的特色是「View Transition API」的應用。

有開發過 SPA (Single-page Application) 網頁的朋友應該都知道,其如同原生應用程式 (native apps) 般的即時換頁體驗是 SPA 的一個強大優勢。而 Astro 採取了較為傳統的 MPA (Multi-page Application) 模式,每次切換頁面的時候都會重新載入一個全新的頁面,在使用者體驗上就沒有 SPA 那麼流暢。

為了解決這個先天缺陷,引入瀏覽器原生的 View Transition API 可以在不增加 JS bundle 的前提之下,大大的提升 MPA 的使用者體驗。

在 Astro 3.0 版本中,View Transition API 正式成為了一個 stable feature,我們可以用短短的兩行程式碼,輕鬆將換頁過場動畫加入到我們的網頁中。從下面這個範例可以看到,MPA 要做到跟原生應用程式類似的體驗不是不可能的!建議可以直接到 Demo 操作看看會更明白實際的效果。

此外,如果想要更深入了解除了引入瀏覽器原生的 View Transition API 之外,Astro 還在底層做了哪些事來達成更好的使用者體驗,可以閱讀這篇文章:Experiments with Astro and the Shared Element Transition API

開始開發吧!

開始一個新的 Astro 專案

首先在終端機執行以下指令來啟動一個新的 Astro 專案:

# create a new project with npm
npm create astro@latest

接著他會問你一些問題,回答完後等待所有 dependencies 下載完畢後就有一個新的 Astro 專案啦。

檔案架構與路由設定

打開新的 Astro 專案,會發現到 src 資料夾下面有三個子資料夾:pageslayoutscomponents,以下針對這三個資料夾做簡單的介紹:

  • pages:放在 pages 資料夾中的子資料夾會自動形成網站的路由,以我的作品集為例,以下的資料夾結構就會形成 https://rooturlhttps://rooturl/frhttps://rooturl/zh 三個路徑。
  • layouts: 我們可以把要應用在頁面的 layout 放在這個資料夾中,主要內容的部分則使用 <slot> 作為替代。
// src/layouts/MySiteLayout.astro
---
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<BaseHead title={title}/>
</head>
<body>
<nav>
<a href="#">Home</a>
<a href="#">Posts</a>
<a href="#">Contact</a>
</nav>
<h1>{title}</h1>
<article>
<slot /> <!-- 主要內容會在這邊 -->
</article>
<Footer />
</body>
</html>
// src/pages/index.astro
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Home Page">
<p>My page content, wrapped in a layout!</p>
</MySiteLayout>
  • components: 在這個資料夾中,我們可以放置 .astro 檔案或是其他 UI 框架的檔案 (例如 .vue, .jsx 等等)

.astro 特殊語法

除了 0 JavaScript 所帶來的高速使用者體驗之外,Astro 團隊也在開發者體驗上下足了苦功,其中之一就是他們所設計的 .astro 語法。

.astro 檔案中,我們可以在兩個 --- 中間撰寫 JavaScript 變數,並在中間的 JSX-like HTML template 中使用。需要注意的是,在兩個 --- 中間的程式碼只會在伺服器端執行,因此我們無法在這段程式碼中撰寫互動性的效果。

若我們要為元素添加樣式,則可以直接將 css 樣式撰寫在 <style> tag 中。寫在 <style> 中的樣式預設都是 scoped,因此不需要擔心樣式全域汙染的問題。

// src/components/MyAstroComponent.astro
---
const hello = "world"
const animals = ["Dog", "Cat", "Rabbit"];
const visible = true;
---
<h1>{hello}</h1>
<ul>
{animals.map((animal) => (
<li>{animal}</li>
))}
</ul>
<div>
{visible && <p>Show me!</p>}
{visible ? <p>Show me!</p> : <p>Else show me!</p>}
</div>

<style>
h1 {
color: red;
font-size: 3rem;
}
</style>

若要撰寫互動性的程式碼,例如添加事件監聽器,則可以使用 <script> tag 來撰寫客戶端 JavaScript:

---
---
<button id="button">Click Me</button>
<script>
function handleClick () {
console.log("button clicked!");
}
document.getElementById("button").addEventListener("click", handleClick);
</script>

使用過 Vue 的朋友會對這樣的 script、template、style 的設計感到熟悉,而與 JSX 相似的語法則會讓使用過 React 的朋友容易上手。

Astro VSCode Extension

針對特殊的語法設計,Astro 團隊也有推出專屬的 VScode 插件,讓我們在開發 Astro 元件時可以有適當的 syntax highlight。

引入喜歡的前端框架

前面有提到,我們可以直接在 .astro 檔案中引入用其他框架所寫的元件,不過在引入之前,必須先安裝對應的 integration,假設我們要在專案中寫 React 元件:

  1. 執行以下指令來新增 React intergration:
npx astro add react

2. 寫好你的 React 元件,並在 .astro 檔案中引入:

// src/pages/static-components.astro
---
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<html>
<body>
<h1>Use React components directly in Astro!</h1>
<MyReactComponent />
</body>
</html>

3. 要注意的是,在 Astro 中,「所有的框架元件預設都只會在伺服器端渲染」以避免將不必要的 JS 傳送到客戶端,因此所有的元件預設都是「靜態元件」!但如果我們的元件是互動性的該怎麼辦?

Astro 提供了一些特殊的 client directives 來控制我們何時要載入該元件的 JavaScript:

  • client:load:當頁面載入的時候,同時載入該元件的 JS。
  • client:visible:只有當使用者滑動頁面到該元件進入 viewport 時,才載入該元件的 JS。
  • client:only="framework":使用這個 directive 表示該元件並不會經歷在伺服器端渲染的過程,因此必須傳入對應的框架名稱以確保 JS 正確載入。
// src/pages/interactive-components.astro
---
import InteractiveButton from '../components/InteractiveButton.jsx';
import InteractiveCounter from '../components/InteractiveCounter.jsx';
import InteractiveModal from "../components/InteractiveModal.svelte"
---
<InteractiveButton client:load />
<InteractiveCounter client:visible />
<InteractiveModal client:only="svelte" />

4. 如果需要的話,我們甚至可以在同一個專案中引入以不同 UI 框架撰寫的元件 (雖然好像通常不會有這樣做的情境 🤔):

---
import MyReactComponent from '../components/MyReactComponent.jsx';
import MySvelteComponent from '../components/MySvelteComponent.svelte';
import MyVueComponent from '../components/MyVueComponent.vue';
---
<div>
<MySvelteComponent />
<MyReactComponent />
<MyVueComponent />
</div>

貼心的錯誤提示畫面

在開發者體驗方面,Astro 也很貼心的準備了友善的錯誤提示畫面,讓開發者面對錯誤時能不那麼感到懼怕!

---
const hello = "world";
const animals = ["Dog", "Cat", "Rabbit"];
---
<!-- 這邊少打了一個字母 -->
<h1>{hell}</h1>
<ul>
{animals.map((animal) => <li>{animal}</li>)}
</ul>
可愛友善的錯誤訊息畫面
可愛友善的錯誤訊息畫面

使用 markdown 格式文件

Markdown Frontmatter 與 Layout

既然是標榜「內容」為中心的框架,處理 markdown 格式的文件是必備的功能,而 Astro 這個部分也整合得非常好,可以直接在資料夾中新增 markdown 格式文件並引入,不需要額外的設定。例如我們在 /src/pages/ 中直接新增一個 post-1.md 的文件如下:

---
layout: ../../layouts/BlogPostLayout.astro
title: Astro in brief
author: Himanshu
description: Find out what makes Astro awesome!
---
# Hi there!

This Markdown file creates a page at `your-domain.com/page-1/`
It probably isn't styled much, but Markdown does support:

- **bold** and _italics._
- lists
- [links](https://astro.build)
- and more!

在兩個 --- 之間的是我們文章的 frontmatter,可以將文章的重要訊息與主要的內容分開,在下方我們則能直接使用 markdown 語法來撰寫文章內容。注意到最上面我們引入了一個 layout 檔案,它被我們定義在 /src/layouts 中:

// src/layouts/BlogPostLayout.astro
---
const {frontmatter} = Astro.props;
---
<html>
<!-- ... -->
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<p>{frontmatter.description}</p>
<slot /> <!-- Markdown content is injected here -->
<!-- ... -->
</html>

定義在 markdown 文件上方的 frontmatter 可以透過 props 的形式取出,並在 layout 中使用。此外,我們也能直接在 <style> tag 中撰寫 markdown 文件的 CSS 樣式

Content Collections

在 Astro 2.0 推出了 Content Collections 的功能,整合了 Zod Schema Validation,讓我們用 type safe 的方式處理 markdown 內容。若要使用 Content Collections 的方式來管理 markdown 文件,須將文件放置在特殊的資料夾 src/content 中,並指定該 Collection 的名稱如下:

接著,我們可以定義一個 src/content/config.ts 設定檔,為 Collection 加上 schema checking。如此一來,Astro 就會自動檢查文件內容是否符合 schema,在開發時也會有屬性的智慧提示。 (註:這個 config 檔案是非必要的,但如果要對文件採取檢查的話就需要設定!):

// 1. Import utilities from `astro:content`
import { z, defineCollection } from 'astro:content';

// 2. Define a `type` and `schema` for each collection
const blogCollection = defineCollection({
type: 'content', // v2.5.0 and later
schema: z.object({
title: z.string(),
tags: z.array(z.string()),
image: z.string().optional(),
}),
});

// 3. Export a single `collections` object to register your collection(s)
export const collections = {
'blog': blogCollection,
};

Build and Deploy

因為預設是 SSG 靜態網頁,因此在打包建置完成後可以輕鬆的部屬到任何靜態部屬平台,如 github pages、Netlify 等等。如果要使用 SSR 產生動態內容的話,則必須額外整合 Adapter 並部屬到相關平台,詳情請見官方文件。這邊將以 Netlify 為例,示範部屬靜態網頁的過程:

  1. 至 Netlify 官方網站上註冊/登入,並進入帳號儀表板
  2. Sites 區塊點選 Add new site,並在下拉選單選取 Import an existing project
  3. 選擇專案的 repository 引入,Netlify 會自動偵測專案內容並填入適當的部屬設定。我們要確認以下的設定正確無誤:
  • Build Command: astro build or npm run build
  • Publish directory: dist

4. 接著等待網站部屬完成,就能在網站頁面看到公開網址、部屬紀錄等資訊,並且能在 Site configuration 中做進一步的設定

5. 下一次只要有新的 commit 被推上該 repo,Netlify 就會重新自動部屬

其他

礙於文章的篇幅,沒辦法在這邊介紹所有的 Astro 特色和開發基礎,若有任何疑問都建議先參考 Astro 的官方文件,編寫的非常清晰易懂。此外這邊提幾個我在開發作品集網站時有參考的內容:

結語

Astro 的未來發展

Astro 1.0 最早在 2022 年推出,兩年後的今天已經推出了 4.0 版本,今年更計畫推出專屬的部屬服務 Astro Studio,未來發展相當值得期待。

之所以會有這麼快速的成長,一方面是找到了明確的定位 (以內容為中心) 以及相對應的技術策略 (0 JS、島嶼架構),一方面也因為採取了「framework agnostic」,吸引了很多本來只專注在單一框架的前端工程師、或是未曾使用前端框架的其他網頁工程師。

此外,前端圈近期吹起的「back to server trend」(例如關於 React Server Components, HTMX 的討論) 也讓人反思前端開發中,大量的 JS 是否有必要,畢竟除了少數的複雜網頁應用,例如 Facebook、Figma 等,這世界上大多數的網頁都是靜態網頁!雖然這並不代表未來前端工程師的職業就會消失或被取代,但身為前端工程師,需要謹記在心的是在 JavaSciprt、React 的世界之外還有好大的一片天地值得去探索。

個人使用感想

回到個人的使用體驗,我認為 Astro 對於初學者來說相當友善,就算只會原生的 HTML、CSS、JS 也可以掌握現代化的元件化開發流程,而不用先去鑽研各個框架的特殊語法和架構。

不過,雖然在基本的架構上可以很快速的開發高效能的靜態網站,但實際開發上仍有一些限制需要注意。例如,使用不同的框架時,相關套件未必相容,同一功能可能會需要不同的解決方案。例如,我的作品集的 i18n 套件只適用於 Astro 元件,無法在 React 元件中使用;或是 Astro 的專屬 Image 元件,在 React 元件中的圖片就無法享受該元件帶來的優化效果。

總而言之,Astro 仍然是個相當年輕的框架,生態系仍然在發展中,資源不像成熟框架如 Next、Nuxt 豐富,框架本身變化也很快,在一年內從 2.0 升到 4.0。但如果看完文章後,對於 Astro 的架構、效能和開發者體驗有興趣、或是正好有開發靜態網站需求的人,推薦現在就實際去開啟一個新的專案來試看看吧!

參考文章與資源

--

--

Kelly CHI

法文系畢業的前端工程師,致力於打造具有美感和良好用戶體驗的介面,同時也是個愛看冷門電影的骨灰級影迷。