從早期的靜態、單頁的個人網站來看,網路開發已經有了長足的進步。現在,我們有大量不同的語言、框架和內容管理系統可供選擇,這些都是為了滿足每一個可以想象到的利基市場。
這就是Astro出現的地方,它是JavaScript框架領域最新的酷兒之一。
由Fred K. Schott和其他一些貢獻者建立的Astro已經迅速成為開發社羣的最愛。它是一個多合一的框架,工作起來很像一個靜態網站生成器。
在這篇文章中,我們將解釋為什麼這麼多開發者喜歡Astro,並選擇它而不是其他解決方案。我們還將指導你如何使用該框架建立一個基於markdown的部落格。
什麼是Astro?
Astro
Astro,或Astro.js,是一個流行的靜態網站生成器,為那些想建立內容豐富、執行快速和流暢的網站的人設計的。它的輕量級性質、直觀的結構和溫和的學習曲線使它對所有經驗水平的開發者都有吸引力。
儘管Astro佔地面積小,但它配備了強大的工具,大大增加了網站的靈活性,為你節省了內容和主題管理的時間。此外,它還為開發者提供了與他們喜歡的框架一起使用Astro的選擇–這對已經有很多喜歡的經驗豐富的編碼員來說是一個很有吸引力的前景。
以下是Astro從人群中脫穎而出的一些方法:
- 孤島架構:Astro將您的使用者介面(UI)提取成較小的、孤立的元件,稱為 “Astro島”,可以在任何頁面上使用。未使用的JavaScript被替換成輕量級的HTML。
- 零JavaScript(預設):雖然你可以使用所有你想要的JavaScript來建立你的網站,但Astro將試圖通過為你轉錄程式碼來部署零JavaScript到生產中。如果你關注的是網站速度,這是一個完美的方法。
- 包括SSG和SSR:Astro開始時是一個靜態網站生成器,但隨著時間的推移,它成為一個同時使用靜態網站生成(SSG)和伺服器端渲染(SSR)的框架。而且你可以選擇哪些頁面使用哪種方法。
- Framework-agnostic:當使用Astro時,你可以使用任何你喜歡的JavaScript框架–甚至同時使用多個框架。(我們將在本文後面更詳細地討論這個問題)。
更重要的是,Astro是邊緣就緒的,這意味著它可以隨時隨地、輕鬆地部署。
準備好了解更多了嗎?那就讓我們深入瞭解一下Astro是如何工作的。
Astro的結構
在我們進一步冒險之前,重要的是要了解Astro是如何設定的,以便你能有效地使用它。讓我們看一下Astro的核心檔案結構:
├── dist/ ├── src/ │ ├── components/ │ ├── layouts/ │ └── pages/ │ └── index.astro ├── public/ └── package.json
正如你所看到的,結構本身相當簡單。然而,有一些關鍵點你應該記住:
- 我們專案的大部分內容都住在src資料夾中。你可以將你的元件、佈局和頁面安排在子資料夾中。你可以新增額外的資料夾以使你的專案更容易瀏覽。
- public資料夾用於存放構建過程之外的所有檔案,如字型、圖片或robots.txt檔案。
- dist資料夾將包含你想在生產伺服器上部署的所有內容。
接下來,讓我們更深入地瞭解Astro的主要組成部分:元件、佈局和頁面。
元件
元件是可重複使用的程式碼塊,可以包含在你的網站上,類似於WordPress中的短程式碼。預設情況下,它們的副檔名為.astro,但你也可以使用由Vue、React、Preact或Svelte構建的非Astro元件。
下面是一個簡單元件的例子–在這個例子中,是一個包含 h2
的類 div
標籤:
<!-- src/components/Wbolt.astro --> <div class="wbolt_component"> <h2>Hello, Wbolt!</h2> </div>
而這裡是我們如何將該元件納入我們的網站:
--- import WboltComponent from ../components/Wbolt.astro --- <div> <WboltComponent /> </div>
如上所示,你首先要匯入該元件。只有這樣,它才能被包含在頁面上。
現在是時候給我們的元件新增一些屬性了。讓我們從 {title}
開始。屬性:
--- const { title = 'Hello' } = Astro.props --- <div class="wbolt_component"> <h2>{title}</h2> </div>
這裡是我們的資產如何實現的:
--- import WboltComponent from ../components/Wbolt.astro --- <div> <!-- This shows "Good day" --> <WboltComponent title="Good day"/> <!-- This shows "Hello" --> <WboltComponent /> </div>
很簡單,對嗎?
你可能已經意識到,Astro元件的真正力量在於它們的全域性性和可重用性。它們使你能夠通過編輯幾行程式碼來對你的整個網站進行全面的改變,這可以為你節省無數的時間,否則這些時間將花在乏味的、痛苦的文字替換上。
佈局
現在,讓我們來談談佈局。除了他們熟悉的主題功能外,Astro中的佈局也是可重複使用的元件,但他們是作為程式碼包裝器使用的。
看一下這個例子吧:
--- // src/layouts/Base.astro const { pageTitle = 'Hello world' } = Astro.props --- <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width"> <title>{pageTitle}</title> </head> <body> <main> <slot /> </main> </body> </html>
注意這裡的 <slot />
標籤。在Astro中, <slot />
元素充當了實際HTML標籤和內容的佔位符。
讓我們看看它的作用。
下面的程式碼顯示了我們的 <slot />
標籤被替換成了我們想要的程式碼,所有這些都被我們的Base.astro佈局包裹著:
--- import Base from '../layouts/Base.astro'; --- <Base title="Hello world"> <div> <p>Some example text.</p> </div> </Base>
正如你所看到的,我們的 <slot />
標籤被它所代表的HTML所取代,這就是:
<div> <p>Some example text.</p> </div>
正如你所看到的,佈局,像元件一樣,允許你在你的網站上重複使用大塊的程式碼,簡化了更新全域性內容和設計的挑戰。
頁面
頁面是一種特殊型別的元件,負責路由、資料載入和模板設計。
Astro使用基於檔案的路由來生成頁面,而不是動態路由。基於檔案的方法不僅消耗較少的頻寬,而且還可以省去你手動匯入元件的麻煩。
下面是一個定義路由的例子:
src/pages/index.astro => yourdomain.com src/pages/test.astro => domain.com/test src/pages/test/subpage => domain.com/test/subpage
有了這些路線,我們的結果主頁將呈現如下:
<!-- src/pages/index.astro --> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width"> <title>Hello World</title> </head> <body> <h1>Hello, Wbolt</h1> </body> </html>
但我們已經知道如何使用佈局,所以讓我們把它轉換為全球通用的東西:
--- import Base from '../layouts/Base.astro'; --- <Base> <h1>Hello, Wbolt</h1> </Base>
這樣–就乾淨多了。
我們將在本文後面更詳細地討論Astro的路由問題,但現在,讓我們進入有趣的東西:網站建設和定製。
定製和擴充套件Astro
現在是時候學習如何定製你的Astro網站了! 我們將使用Markdown集合、路由、影象處理以及與React的整合來構建和個性化我們的靜態網站。
Markdown集合
在2.0版本中,Astro引入了一種比以前更好的方式來維護Markdown內容。由於有了集合,我們可以確保我們所有的前題資料都被包括在內,並且有正確的關聯型別。
最近,在2.5版本中,他們增加了一種可能性,也可以把JSON和YAML檔案作為集合來管理。
準備好上手了嗎?
首先,把你所有的Markdown文章放在src/content/collection_name資料夾中。我們將為這個專案建立一個部落格集合,所以在我們的演示中,這個資料夾將是 src/content/blog。
現在是時候在我們的 src/content/config.ts 檔案中定義所有需要的 frontmatter 欄位。我們的部落格將需要以下內容:
title
(字串)tags
(陣列)publishDate
(時間)image
(字串,可選)
這就是所有東西放在一起的樣子:
import { z, defineCollection } from 'astro:content'; const blogCollection = defineCollection({ schema: z.object({ title: z.string(), tags: z.array(z.string()), image: z.string().optional(), publishDate: z.date(), }), }); export const collections = { 'blog': blogCollection, };
這就是我們的article-about-astro.md Markdown檔案的內容:
--- title: Article about Astro tags: [tag1, tag3] publishDate: 2023-03-01 --- ## Tamen risit Lorem *markdownum flumina*, laceraret quodcumque Pachyne, **alter** enim cadavera choro.
的確,我們的Markdown檔案沒有什麼特別之處。但這裡有一些隱藏的魔法,如果我們打錯了字,就會顯現出來。
比如說,我們沒有輸入 publishDate
,而是不小心輸入了 publishData
。在這種拼寫錯誤的情況下,Astro會丟擲一個錯誤:
blog → article-about-astro.md frontmatter does not match collection schema. "publishDate" is required.
很神奇,對嗎?這個靈巧的功能可以幫助我們在幾秒鐘內找到與前沿問題有關的錯誤。
我們需要新增的最後一件事是一個顯示我們資料的頁面。讓我們在 src/page/blog/[slug].astro 建立一個檔案,程式碼如下:
--- import Base from '../../layouts/Base.astro'; import { getCollection } from 'astro:content'; export async function getStaticPaths() { const blogEntries = await getCollection('blog'); return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } const { entry } = Astro.props; const { Content } = await entry.render(); --- <Base> <h1>{entry.data.title} </h1> <Content /> </Base>
感謝 getStaticPaths
,Astro將為部落格集合中的每篇文章建立所有的靜態頁面。
我們現在唯一缺少的是我們所有文章的列表:
--- import Base from '../../layouts/Base.astro'; import { getCollection } from 'astro:content'; const blogEntries = await getCollection('blog'); --- <Base> <ul> {blogEntries.map(item => <li> <strong><a href={'/blog/' + item.slug}>{item.data.title}</a></strong></li>)} </ul> </Base>
正如你所看到的,使用集合使這項任務變得非常簡單。
現在,讓我們來建立一個資料型別集合。首先,我們必須再次開啟 src/content/config.ts 檔案並新增一個新的資料集合:
import { z, defineCollection, referenece } from 'astro:content'; const blogCollection = defineCollection({ type: 'content', schema: z.object({ title: z.string(), tags: z.array(z.string()), image: z.string().optional(), publishDate: z.date(), author: reference('authors') }), }); const authorsCollection = defineCollection({ type: 'data', schema: z.object({ fullName: z.string(), country: z.string() }), }); export const collections = { 'blog': blogCollection, 'authors': authorsCollection, };
除了建立一個新的集合,我們還在blogCollection中新增了author的引用。
是時候建立一個新的作者了。我們必須在content/authors.json中建立一個名為maciek-palmowski.json的檔案:
{ "fullName": "Maciek Palmowski", "country": "Poland" }
剩下的最後一件事是在我們的Post中抓取這些資料。要做到這一點,我們需要使用getEntry:
--- import Base from '../../layouts/Base.astro'; import { getCollection, getEntry } from 'astro:content'; export async function getStaticPaths() { const blogEntries = await getCollection('blog'); return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } const { entry } = Astro.props; const author = await getEntry(entry.data.author); const { Content } = await entry.render(); --- <Base> <h1>{entry.data.title}</h1> <h2>Author: {author.data.fullName}</h2> <Content /> </Base>
路由
Astro有兩種不同的路由模式。我們已經瞭解了第一種模式–靜態(基於檔案的)路由–當我們在前面介紹頁面時。
現在我們要把重點轉移到動態路由上。
使用動態路由引數,你可以指示一個Astro頁面檔案,以自動建立具有相同結構的多個頁面。當你有很多特定型別的頁面時,這很有用(想想作者簡介、使用者資料、文件文章等等)。
在接下來的例子中,我們將致力於為我們的作者生成生物頁面。
在Astro預設的靜態輸出模式下,這些頁面是在構建時生成的,這意味著你必須預先確定獲得相應檔案的作者名單。另一方面,在動態模式下,頁面是在請求時為任何匹配的路線生成的。
如果你想傳遞一個變數作為你的檔名,在它周圍加上括號:
pages/blog/[slug].astro -> blog/test, blog/about-me
讓我們使用src/page/blog/[slug]檔案中的程式碼深入瞭解一下:
--- import Base from '../../layouts/Base.astro'; import { getCollection } from 'astro:content'; export async function getStaticPaths() { const blogEntries = await getCollection('blog'); return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } const { entry } = Astro.props; const { Content } = await entry.render(); --- <Base> <h1>{entry.data.title}</h1> <Content /> </Base>
getStaticPaths
路由負責生成所有靜態頁面。它返回兩個物件:
params
:用來填補我們的URL中的括號props
:我們要傳遞給頁面的所有值
就這樣,你的頁面生成已經搞定了。
影象處理
在談論高效能網站時,我們不能不提到現代圖片格式、正確的大小調整方法和懶惰載入。
幸運的是,Astro在這裡也為我們提供了保障。多虧了 @astrojs/image
包,我們可以在幾分鐘內介紹上述所有內容。
安裝該包後,我們可以訪問兩個元件: Image
和 Picture
。
Image
元件用於建立優化的 <img />
標記。以下是一個示例:
--- import { Image } from '@astrojs/image/components'; import heroImage from '../assets/hero.png'; --- <Image src={heroImage} format="avif" alt="descriptive text" /> <Image src={heroImage} width={300} alt="descriptive text" /> <Image src={heroImage} width={300} height={600} alt="descriptive text" />
類似地,Picture
元件建立了一個優化的 <Picture/>
元件:
--- import { Picture } from '@astrojs/image/components'; import hero from '../assets/hero.png'; --- <Picture src={hero} widths={[200, 400, 800]} sizes="(max-width: 800px) 100vw, 800px" alt="descriptive text" />
SSG vs SSR
預設情況下,Astro作為一個靜態網站生成器執行。這意味著所有的內容都被轉換為靜態HTML頁面。
雖然從許多方面來看這是一個完美的方法(尤其是與速度有關的),但我們有時可能更喜歡一個更動態的方法。例如,如果你想為每個使用者建立一個單獨的個人資料頁面,或者如果你的網站上有成千上萬的文章,每次重新渲染所有內容都會太耗時。
幸運的是,Astro也可以作為一個完全的伺服器端渲染的框架來工作,或者以兩者之間的混合模式工作。
要啟用側邊的SSR,我們需要在astro.config.mjs中新增以下程式碼:
import { defineConfig } from 'astro/config'; export default defineConfig({ output: 'server' });
這是標準的方法。
混合方法意味著在預設情況下,除了新增了 export const prerender = true
的頁面外,一切都會動態生成。
在Astro 2.5中,也有可能將靜態渲染設定為預設,並手動選擇動態路由。
由於這些,我們可以,例如,建立一個完全靜態生成的網站,有動態的登入和個人資料頁面。很好,對嗎?
你可以在官方文件中讀到更多關於這方面的資訊。
整合其他JavaScript框架
Astro的另一個驚人的功能允許你帶上你最喜歡的框架,並與Astro協同使用。你可以將Astro與React、Preact、Svelte、Vue、Solid或Alpine混合使用(關於所有的整合,見Astro的 “新增整合” 文件)。
比方說,我們想使用React。首先,我們需要通過在npm中執行以下程式來安裝整合:
npx astro add react
現在React已經被整合,我們可以建立一個React元件。在我們的例子中,它將是 src/components/ReactCounter.tsx 中的計數器元件:
import { useState } from 'react'; /** A counter written with React */ export function Counter({ children }) { const [count, setCount] = useState(0); const add = () => setCount((i) => i + 1); const subtract = () => setCount((i) => i - 1); return ( <> <div className="counter"> <button onClick={subtract}>-</button> <pre>{count}</pre> <button onClick={add}>+</button> </div> <div className="counter-message">{children}</div> </> ); }
最後但並非最不重要的是,我們需要用以下程式碼將計數器放在我們的頁面上:
--- import * as react from '../components/ReactCounter'; --- <main> <react.Counter client:visible /> </main>
voilà:你的React元件已被無縫地整合到你的網站。
如何使用Kinsta部署Astro
現在是時候把我們的Astro網站放到網上了。幸運的是,Kinsta是一個完美的主機,可以快速而無痛地進行部署。
首先,為你的網站檔案建立一個GitHub倉庫。如果你還沒有準備好使用自己的檔案,你可以克隆我們團隊建立的這個Astro入門網站模板。
Kinsta的Astro啟動網站模板的GitHub倉庫
一旦你的Repo準備好了,請登入MyKinsta,在左邊選擇Applications,然後從紫色的Add service下拉選單中選擇Applications。
在MyKinsta中新增一個應用程式
最後一步是向Kinsta提供你的構建和部署細節。
大部分你會被問到的問題,比如Process name和Payment method,都會有明顯或簡單的答案。請注意,如果你選擇不填 “Start command” 命令,Kinsta會自動指定 npm start
作為該命令。
如果你不確定如何應對任何其他提示,請參考Kinsta的文件,瞭解特定領域的指導和Astro部署例項。你也可以檢視Astro自己的在Kinsta部署的指南。
一旦你完成了輸入你的構建細節,點選Confirm payment method按鈕來初始化你的構建。
這就是了! 你現在有了一個用Astro框架建立的實時的、功能齊全的靜態網站。
我們實時的Astro主頁
你可以在你的MyKinsta賬戶中的Deployments下找到你的實時URL和其他部署細節。
一個成功的Astro部署
小結
Astro清晰的結構、簡單的語法和全域性元件使得構建和執行一個應用程式非常容易。它的輕量級性質和靜態和動態路由的雙重使用極大地提高了網站的響應速度,而它與其他JavaScript框架合作的能力,也使它對有經驗的編碼人員更有吸引力。
如果你的目標是建立一個內容豐富的網站,快速載入,授予模組化功能,並提供靜態和動態生成,那麼Astro可能是你的正確選擇。
評論留言