在蓋「Moment:看電影看劇時,聽到喜歡的音樂」專案的時候,覺得輕量級的網站,用 Markdown 語法可以滿足快速、便於更新內容的需求,找到了 Astro 和 Remix。
Astro 目前以幾乎每天更新一次的頻率快速發展中,2022 年 8 月進入 1.0 穩定版本。這篇文章不會再講一次 Astro 文件提到的功能,而是文件上找不到參考,只好到處從社群爬文記下來的做法。
"astro": "^1.1.0"
Moment 是搭配 Solid.js
,用來處理影片播放互動,後來 Astro 本身幾乎是隱形,跟框架整合得不錯。
首頁有 30 張,平均 24 KB 的 JPG 圖檔,依然可以在 Lighthouse 獲得 4 個 100 綠燈。(慶祝動畫怎麼不見了?)
如果是從 Astro 的 Blog Starter 範例進行修改,基礎是 preact
,使用的體感跟 React 幾乎沒有差別。
接下來的內容
- Astro v.s. Remix
- 區分
<body>
樣式 - 設定 Markdown 語法的樣式
- 加入結構化資料 JSON LD 語法
- 遇到的 Bug
Astro v.s. Remix
Remix 的開發體驗確實很棒,社群也非常活絡。但畢竟是全端套件,如果只是要做靜態網站,以後會優先選擇 Astro。
Astro
- 可以發布到靜態網站服務:Github Pages、Vercel 或 Netlify 都行,也就是可以免費。
- 開發團隊建議使用 Tailwind、SCSS(或者更像 CSS Modules)和 PostCSS。
- 未正式支援
CSS-In-JS
,但社群有整理出可以使用的套件。
Remix
- 可以使用
CSS-In-JS
。 - 雖然同樣可以做成靜態網頁,但 MDX 放在路徑資料夾裡的時候,連路徑名稱都無法取得,目前實用程度不高。
- 無法在(免費)靜態網站服務使用
mdx-bundler
的原因與考究,詳情請看這篇:初次使用 Remix,搭配 mdx-bundler。
區分 <body>
樣式
Astro 的 CSS 是 scoped
,不同頁的樣式不會混在一起。我想要首頁跟內文頁的外觀、版面不同。因此在各自頁面的 <body>
設定:
// pages/index.astro
body {
background-color: lavender;
}
// layouts/BlogPost.astro
body {
background-color: thistle;
}
發現首頁的 <body>
樣式,會被內頁的設定覆蓋:
詢問過 Astro 製作團隊,是他們刻意這樣設計,如果也有不同 <body>
樣式需求,有以下 3 個步驟:
在 <body>
加上屬性來區別頁面
<!-- pages/index.astro -->
<body data-body-style="home">
...
</body>
<!-- layouts/BlogPost.astro -->
<body data-body-style="post">
...
</body>
以 import
匯入全域 CSS 檔案
// Head.astro
import '~/styles/global.css';
設定個別樣式
// ~/styles/global.css
[data-body-style="home"] {
background-color: lavender;
}
[data-body-style="post"] {
background-color: thistle;
}
內容頁,或者說 /layouts
裡的樣式,就不會蓋掉首頁的。
設定 Markdown 語法的樣式
在 Astro 的 pages/
下,無論是使用 .md
或 .mdx
檔案,都可以有等同於 MDX 的功能,將元件輸入至內文。
// LyricSection.astro
<section class="lyricSection">
<slot />
</section>
---
# /pages/bohemian-rhapsody.mdx
layout: LyricSection from '../../components/LyricSection.astro';
---
<LyricSection>
Is this the real life?
Is this just fantasy?
Caught in a landslide
No escape from reality
</LyricSection>
這一段會產生 HTML 語法:
<!-- /bohemian-rhapsody -->
<section class="lyricSection astro-SNPOEJMT">
<p>Is this the real life?</p>
<p>Is this just fantasy?</p>
<p>Caught in a landslide</p>
<p>No escape from reality</p>
</section>
我想要設定 <section>
下面的 <p>
樣式,會發現也是因為 scoped
的緣故,沒辦法直接指定:
// LyricSection.astro
...
<style lang="scss">
.lyricSection {
display: grid;
row-gap: 16px;
p {
margin: 0;
color: hsl(var(--color-midnight1600));
font-size: 1.6rem;
line-height: 24px;
}
}
</style>
必須加上 :global()
才有效果:
// LyricSection.astro
.lyricSection {
...
:global(p) {
...
}
}
加入結構化資料 JSON LD 語法
按照直覺把結構化資料放在 <head> ... </head>
裡面時,會發現無法如預期輸出 JSON LD 語法:
// BaseHead.astro
---
const schema = JSON.stringify({
'@context': 'https://schema.org',
'@graph': [{
'@type': 'WebPage',
url: `${import.meta.env.PUBLIC_HOSTNAME}${canonicalPath}`,
name: title,
datePublished: published,
dateModified: modified
}]
});
---
<script type="application/ld+json">{schema}</script>
0.23.0
版開始,在 <script>
得要讓 HTML 有 Escape 過,才會出現有效的 JSON LD 語法:
// BaseHead.astro
<script type="application/ld+json" set:html={schema} />
遇到的 Bug
在程式碼區塊使用環境變數
在上一段內容,程式碼區塊裡有提到 Astro 的環境變數。
上傳到正式環境才發現:Markdown 區塊裡應該要直接顯示環境變數原始碼的地方,卻把它轉換了,或是出現令人不解的訊息,在使用 SSR 的情況下,甚至會無法 Build 成功。
這個問題已經在 @astrojs/mdx@0.11.2
修正。