我們在前幾講討論 SSG(Static Site Generator)時比較了許多知名的 SSG,包含入門較為簡單的 Jekyll、Hexo,追求效能的 Hugo、Astro,以及需要高互動性的 Gatsby、Next.js 等等。
一切端看我們建立的網站需求為何,我主要的訴求是閱讀居多、搭配少量互動(可參閱我的 PRD),並由於較為熟悉 Node.js 生態,因此最後挑了 Astro 這套 Framework 來作為開發部落格的 SSG,這講就來正式開始介紹 Astro 吧!
Astro 是一套「內容導向、Server-first」的網站框架,也就是先在伺服器把頁面產好再丟給前端的瀏覽器(同時支援 SSR 及 SSG)。
和多數以 SPA(Single Page Application)為主的框架不同,Astro 不會第一次就載入較大的 JavaScript Bundle,而是預設傳送純 HTML,有互動元件才會將「部分」相關的 JavaScript 傳送給瀏覽器。
只有加上 client:*
的指令,Astro 才會自動將此元件綁定相關的 JavaScript,其餘內容仍是完全沒有 JavaScript 的靜態。下面的例子中 <h1 />
標籤為靜態,只有和 <Counter />
相關的 JavaScript 才會被打包起來。
---
// index.astro
import Counter from '../components/Counter.tsx';
---
<h1>Hello Astro</h1>
<Counter client:visible />
*Astro 範例程式
因此 Astro 標榜 Fast by default,據其官網聲稱,Astro 相較於傳統的 React Framework 做出同樣的網站,載入速度快 40%,JavaScript 少 90%。
我們知道從伺服器傳到前端的檔案大小越小,自然傳輸速度也就越快;加上 JavaScript 的量也少,瀏覽器需要執行的腳本少了之後,整體的效能就提高了。
由於網站效能高的緣故,在 SEO 的評分上較其他框架有優勢,也很適合作為部落格、行銷網站或電商平台使用。
Astro 之所以能讓前端載入少量的 JavaScript,是因為引入了他們參與設計的架構:Islands Architecture,可翻譯成島嶼架構、孤島架構,或是小島架構。
島嶼架構的核心概念是將頁面需要互動的區塊做成一座座獨立的島嶼,當瀏覽器載入頁面之後,才選擇性的 Hydrate(將 DOM 與 JavaScript 綁定)這些小島,藉此達成執行更少的 JavaScript 達到必要的互動。
且看一個極簡的頁面,包含標題等靜態內容,以及一個可以往上加的計數器(黃色部分,按鈕加上文字框)。
*簡易計數器範例
Astro 支援 React / Vue / Svelte 等等,這裡就用 React 來做一顆計數器小島。
import { useState } from "react";
export default function Counter() {
const [n, setN] = useState<number>(0);
return (
<div>
<input type="number" value={n} />
<button onClick={() => setN(n + 1)}>+</button>
</div>
);
}
Counter.tsx
我們實作了一個很簡單的計數器,這會是頁面中唯一的互動元件,需要使用到 JavaScript 。
---
import Counter from "../components/Counter.tsx";
---
<h1>簡易計數器</h1>
<a href="/">回到首頁</a>
<Counter client:visible />
index.astro
Astro 在產出頁面的時候,會將 <Counter />
渲染成純 HTML,並包在 <astro-island />
的自訂元素中,如果有 Props 的話,會在其中加入一段 type="application/json"
的序列化資料,供 Hydration 使用。下方是示意程式碼
<astro-island hydration="visible"
component-url="/_astro/Counter.[hash].js"
component-export="default"
renderer-url="/_astro/react.[hash].js">
<div>
<input type="number" value="0" />
<button>+</button>
</div>
<script type="application/json">{}</script>
</astro-island>
<script type="module" src="/_astro/client/[hash].js"></script>
此時 <Counter />
已經顯示出文字框和計數按鈕,但是還沒辦法真的互動。
下一步,此元件島嶼由於瀏覽器的 Observer 監看 hydration="visible"
的小島,當小島進入視窗可見區的時候會觸發事件,下載此元件的 Bundle,也就是在 component-url
所記錄位置的 JavaScript 檔。
最後一步則是 Hydration,將載入的 JavaScript 和 <astro-island />
綁定,此時按鈕就可以點擊,成功執行計數的功能了。