3.2 建立前端專案:HTML/CSS/JavaScript 到 Astro / React 首選框架#
本節目標#
本節先以純 HTML/CSS/JS 建一個最小網站,理解「能被 Pages 部署的東西到底長什麼樣」;接著介紹現代前端框架的選型(本書推薦靜態優先的 Astro,並說明何時改用 React + Vite);最後實際建立 demo 專案,跑通 npm 開發流程,為 3.3 節部署到 Cloudflare Pages 做準備。
起點:沒有框架的最小網站#
Pages 部署的最終產物永遠是一堆靜態檔案。框架只是幫你「生產」這堆檔案的工具。先看最原始的版本:
demo-static/
├── index.html
├── styles.css
└── app.js
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hello Pages</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>我的第一個 Cloudflare Pages 網站</h1>
<p id="clock"></p>
<button id="btn">點我</button>
<script src="app.js"></script>
</body>
</html>
/* styles.css */
body { font-family: system-ui, sans-serif; max-width: 42rem; margin: 3rem auto; }
// app.js —— 唯一的互動「島」,就一個小時鐘
document.querySelector('#btn').addEventListener('click', () => alert('哈囉!'));
setInterval(() => {
document.querySelector('#clock').textContent =
'現在時間:' + new Date().toLocaleTimeString('zh-TW');
}, 1000);
這已經是完整可上線的 Jamstack 網站。純手寫的極限在哪?——當你有一百篇部落格文章:標頭要複製一百次、目錄要手動維護、共用的樣式改一處要改一百個檔案。這時就需要框架 = 一個會產生靜態檔案的編譯器。
選型:靜態優先時代的首選#
選型判準不是「哪個框架最流行」,而是內容網頁與應用程式網頁是兩種物種:
| 你的網站 | 物種 | 推薦 |
|---|---|---|
| 部落格、文件、行銷頁、作品集 | 內容站(controller = 建置時) | Astro ✅ 本書主線 |
| 帳號系統、儀表板、高互動工具 | 應用站(controller = 使用者互動) | React + Vite |
Astro 是 2021 年問世的靜態優先框架,直接實踐 3.1 節的 Islands Architecture:
- 預設輸出零 JavaScript 的純 HTML,互動元件才單獨注水
- Components 不限框架:可以混用原生 HTML、React、Vue、Svelte
- 內建 Markdown 支援——部落格文章就是一個
.md檔
React(配 Vite) 適合「整頁都是互動」的應用:狀態管理(4.1、4.2 節的理論在這裡登場)、豐富生態系(React Router、TanStack Query)。Vite 是 2020 年推出的開發伺服器/打包器,以原生 ES modules 實現毫秒級熱更新。
建立專案:相同心智模型的兩條路#
兩者都用同一套 npm 心智模型:
npm run dev → 本機開發伺服器(改檔即時重載)
npm run build → 編譯出 dist/ 靜態檔 ← 這就是要部署的東西
npm run preview → 本機預覽 build 結果
# 路 A:Astro(本書主線)
npm create astro@latest demo-pages -- --template blog --no-install --no-git
# 路 B:React + Vite
npm create vite@latest demo-pages -- --template react-ts
Astro demo 專案實作#
建立完成後資料夾結構:
demo-pages/
├── astro.config.mjs ← 建置設定:輸出模式、整合
├── package.json
├── public/ ← 原封不動複製到 dist/ 的靜態資產
│ └── favicon.svg
└── src/
├── pages/ ← 檔案即路由(file-based routing)
│ └── index.astro
├── layouts/ ← 頁面外框
├── components/ ← 可重用元件(島)
└── content/ ← Markdown 文章(資料即內容)
先確認建置設定——本書前段以純靜態為主:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
// 部署子路徑情境用;Pages 的 *.pages.dev 直接用預設即可
// 動態資料一率走 API(第 4 章 Pages Functions),前端保持 SSG
});
首頁元件 .astro 檔由三段構成——上:蘊含 TypeScript 的腳本(建置時執行)、中:樣板、下:樣式:
---
// src/pages/index.astro —— 「---」之間的程式在**建置時**跑完,不留 JS 給瀏覽器
import Layout from '../layouts/Layout.astro';
const posts = [
{ slug: 'hello-cloudflare', title: 'Hello, Cloudflare!' },
{ slug: 'jamstack', title: ' Jamstack 是什麼' },
];
---
<Layout title="首頁">
<h1>邊緣上的部落格</h1>
<ul>
{posts.map((p) => (
<li><a href={`/posts/${p.slug}/`}>{p.title}</a></li>
))}
</ul>
</Layout>
---
// src/layouts/Layout.astro
const { title = 'Demo' } = Astro.props;
---
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
</head>
<body>
<header><a href="/">🏠 回首頁</a></header>
<main><slot /></main>
</body>
</html>
跑起來驗證:
npm install # 安裝依賴(node_modules/ —— 永遠不進 Git)
npm run dev # http://localhost:4321
npm run build # 產出 dist/ ,裡面是 .html + 少量 .js
npm run preview # 本機預覽 dist/
npm run build 的關鍵驗證:打開 dist/posts/hello-cloudflare/index.html,搜尋裡面有沒有樣板語法——沒有。所有 {posts.map(...)} 都在建置時被展開成真實 HTML,這就是 SSG 的「先做好」。
若選 React + Vite:結構對照#
npm create vite@latest demo-react -- --template react-ts
cd demo-react && npm install && npm run dev # http://localhost:5173
demo-react/
├── vite.config.ts
├── index.html ← 唯一入口(SPA shell)
└── src/
├── main.tsx ← 掛載 React 樹的起點
├── App.tsx ← 根元件:UI = f(state)(4.1 節主題)
└── components/ ← 元件目錄
對照心智模型:Astro 「檔案即頁面、建置時算好」,React 「單一 shell、瀏覽器裡根據 state 現畫」。兩者 build 出來的都只是靜態檔——對 Pages 而言它們沒有差別,差別只在 JS 多寡與開發體驗。
部署前的 Git 衛生#
Pages 的 Git 整合(3.3 節)以 Git 儲存庫為單一真相來源,先做好 .gitignore:
node_modules/
dist/
.env
.env.*
然後:
git init && git add . && git commit -m "chore: init demo-pages"
小結與練習#
本節從純 HTML 起步,確認 Pages 部署的最終形態就是靜態檔案;以「內容站 vs 應用站」決定框架:主線 Astro(零 JS 預設、群島注水),備選 React+Vite;並用 npm run build 親眼驗證 SSG「樣板在建置時消失」的原理。
練習
- 在
src/pages/新增about.astro(React 則是 react-router 路由),npm run build後在dist/找到對應的 HTML,確認本地 preview 可開。 - 把
posts改成份數3份的 Markdown:體驗 Astro 內建 Markdown 路由,觀察dist/每篇文章的資料夾。 - 進階:在 React 版專案執行
npm run build,用開發者工具 Network 比較「Astro 頁面」與「React 頁面」各自載入的 JS 體積——量化 3.1 節「群島 vs 整頁注水」的差距。
下一節把這個專案推上 Cloudflare Pages,觸發第一條自動 CI。