前端技術日新月異,最初的靜態網站逐漸被由服務端生成的網站所取代,后來又逐漸向客戶端渲染的應用轉變。不過客戶端渲染也存在一些問題,如加載時間變長和搜索引擎優化難度等。Astro 這個新的前端框架結合了服務端渲染和客戶端渲染的優點,可以更好地解決這些問題。
本文就來介紹一下這兩年爆火的前端框架 Astro,它在兩年的時間新增了 30k+ star:
這個前端框架,有點不一樣。
Astro 基本概念
Astro 是一個開源的 JAVAScript 框架,用于在流行的UI框架(如React、Preact、Vue 或 Svelte)之上生成 Web 應用。Astro 的頁面由多個獨立的組件組成。為了提高加載速度,Astro 會在服務端對頁面進行預渲染,并剝離所有 JavaScript,除非將某個組件標記為交互式,此時 Astro 將發送必要的最小量 JavaScript 以實現交互功能。
通過這種策略,Astro 頁面加載速度快,因為在首次渲染時不需要執行任何 JavaScript。在注水的過程中,Astro會將 JavaScript “注入”到組件中,使它們變成動態的。
Astro 歷史發展
Astro 是由 Fred Schott 和 Nate Moore 創建,最初用于構建快速靜態內容站點,如博客和落地頁等。它最初的獨特優勢就是簡單易用。可以從各種來源獲取內容,包括 API、CMS、MDX 文件或 Markdown 文件,并在 Astro 站點上展示。
Astro 最初的設計并非與 React 或 Vue 等競爭,而是為了支持互操作性。簡而言之,就是可以在 Astro 中使用喜歡的工具!它提供了對 React、Vue、Svelte 和 TAIlwind css 等前端工具的一流支持。
然而,Astro 真正的亮點是名為島嶼的前端架構范式轉變。Astro的島嶼架構能夠提高應用的速度,它將 UI 拆分為更小的、隔離的組件,并在靜態頁面中部分啟動交互式組件,這是一個大膽的創新。
現在,Astro 已經發展成一個現代 Web 框架,可用于構建快速的多頁面應用、動態服務器端點和注重內容性能的網站。盡管保留了簡單性和核心功能,如服務器端點、內容集合、視圖過渡以及出色的開發者體驗,但 Astro 正不斷演進,成為一個功能強大的現代Web應用程序框架。
Astro 工作原理
Astro 的核心是其島嶼架構。那么 Astro 中的島嶼是如何運作的呢?這就不得不說它的島嶼架構和部分水合了。
我們知道,客戶端和服務端是向用戶提供應用的兩個主要參與者:
下面來看一個在客戶端上重新水合的服務端渲染的應用的例子。
服務端渲染簡化的過程如下:
- 在服務端渲染應用
- 在客戶端上重新水合整個應用
這是描述部分水合作用的簡單方法。我們沒有對整個應用進行水合處理,而是專注于應用的較小交互部分并獨立地對這些部分進行水合處理:
Astro 島嶼就是嵌入靜態html頁面中的交互式UI組件。一個頁面上可以存在多個島。每個島都是通過獨立的部分水合方式進行渲染。也就是說,每個島都是獨立水合的。
那這么做有什么好處呢?
通過利用島嶼,Astro 應用可以具有出色的初始加載時間,而不會受到 JavaScript 的限制。大部分站點保持靜態狀態,只有在初始頁面加載之后需要時才會對交互部分或島進行水合。
Astro 關鍵功能
上面介紹了Astro 的島嶼架構和部分水合,下面來看看 Astro 的核心功能,以更有效的使用它。
組件
Astro 應用的最小單位是組件。組件構成了每個 Astro 應用的基礎:
Astro 組件文件都以 .astro 結尾。
與大多數其他前端框架類似,組件內的抽象程度由開發者自己決定。例如,組件可以是 UI 的一小部分可重用的組件,例如頁眉或頁腳,或者組件可以足夠大以構成整個頁面或布局。
來看看下面的 Hello World Astro 組件:
// HelloWorld.astro
---
const name = "前端充電寶"
---
<h1>Hello world, {name} </h1>
可以看到,Astro 包含兩個部分:組件腳本和組件模板
組件腳本是包含在 ---
虛線之間的部分,與 Markdown 前面的內容塊相同。在腳本部分中,默認情況下,可以編寫任何有效的 JavaScript 和 TypeScript!在上面的例子中,定義了一個 name
變量:
---
const name = "前端充電寶"
---
組件模板就是定義組件 HTML 的地方。如果組件需要向瀏覽器渲染一些 UI 元素或 HTML,可以在此處定義它。在上面的例子中,定義了以下內容:
<h1>Hello world, {name} </h1>
Astro 組件模板決定了組件的 HTML 輸出并支持純 HTML!不過,Astro 組件模板語法是 HTML 的超集。它添加了強大的插值功能,因此可以充分利用 JavaScript 和 TypeScript 來編寫組件模板。
除了簡單的字符串插值之外,組件模板語法還支持很多功能,例如添加<style>
和<script>
標簽、利用動態屬性、條件渲染、動態HTML等。以下是其中一些功能的演示:
---
const isActive = false
---
<h1>前端充電寶</h1>
{/** 條件渲染 **/}
{isActive && <p>Hello World from 前端充電寶</p>}
{/** 樣式:默認情況下樣式是有作用域的 **/}
<style>
h1 {
color: red,
}
{/** 添加腳本 **/}
<script>
<!-- 也可以在此處編寫TypeScript。Astro 原生支持 TypeScript -->
const header = document.querySelector("h1")
header.textContent = "Updated LogRocket Header"
</script>
頁面和路由
Astro 應用的入口點是頁面,它利用基于文件的路由方式來管理頁面。
假設正在構建一個具有兩個不同路由的 Web 應用:index.html 和 about.html,那該如何表示呢?
Astro 項目的 src/pages/ 子目錄中的每個文件對應一個頁面。在這個例子中,需要兩個頁面 :src/pages/index.astro 和 src/pages/about.astro:
在構建應用時,可以通過像npm build這樣的簡單命令來進行構建,index.astro和about.astro將被構建成應用中相應的index.html和about.html文件。
Astro 是一個多頁面 Web 框架。也就是說,默認情況下,每個路由都對應一個單獨的 HTML 文檔。
可以按照以下步驟來嘗試使用 Astro:
- 通過運行 npm create astro@latest 命令創建一個新的 Astro 項目。
- 按照終端中的提示設置新項目。
- 使用 astro dev 命令啟動應用。
- 在代碼編輯器中打開項目。
- 查看 src/pages 目錄并創建兩個新頁面:index.astro 和 about.astro。
- 通過 astro build 構建應用。
- 查看構建輸出并觀察兩個單獨的 HTML 文件——每個路徑一個。
在 Astro 中,不僅可以使用 .astro 組件作為頁面,還可以使用以下方式構建頁面:
- Markdown 和 HTML 文件
- 安裝了 MDX 集成的 MDX 文件
- JavaScript 或 TypeScript 作為靜態文件或 API 端點
動態路由
上面我們討論了頁面和 HTML 輸出之間的一對一映射,不過,在 Astro 中也可以通過一個頁面處理多個路由。
Astro 頁面可以在文件名中指定動態路由參數,這將生成多個匹配的頁面。例如,構建一個博客應用,其中每篇博客都有自己的路由,可以通過以下方式表示該頁面:
src/pages/blogs/[blog].astro
注意,組件文件名中的方括號:[blog].astro。在靜態模式下,所有路由都必須在構建時確定。因此,動態路由必須導出 getStaticPaths() 方法,該方法返回具有 params 屬性的對象數組。這告訴 Astro 在構建時生成哪些頁面。
例如:
// src/pages/blogs/[blog.astro]
---
export function getStaticPaths() {
return [
{params: {blog: "how-to-learn-astro"}},
{params: {blog: "how-to-learn-ai"}},
{params: {blog: "how-to-learn-astro-ai"}},
]
}
// 可以從 Astro 全局對象中解構參數
const { blog } = Astro.params
---
<h1> Hello Blog, {blog} </h1>
當構建此應用時,src/pages/blogs/[blog.astro] 將生成三個博客頁面:
- /blogs/how-to-learn-astro.html
- /blogs/how-to-learn-ai.html
- /blogs/how-to-learn-astro-ai.html
Astro 還支持服務端渲染,在這種情況下,路由是在運行時而不是構建時生成的。這意味著不需要指定 getStaticPaths() 方法,并且頁面可以大大簡化,如下所示:
// src/pages/blogs/[blog.astro]
---
// 可以從 Astro 全局對象中解構參數
const { blog } = Astro.params
---
<h1> Hello Blog, {blog} </h1>
在實踐中,使用SSR,這里的博客參數很可能是博客的唯一標識符,例如 ID。當用戶嘗試查看 /blogs/id 時,可以通過 Astro.params 獲取 id 并在服務端獲取所需的博客內容。
布局
除了正常的組件和特殊的組件“頁面”,下面來了解另一種類型的組件:布局。
大多數 Web 應用都包含一些可重用的結構,這些結構為頁面提供了結構,例如頁眉、頁腳和側面導航元素。這些可以被抽象為可重用的 Astro 組件,稱為布局。
可以通過在 src/layouts 目錄中添加 Astro 組件來創建布局組件:
在布局組件中,可以抽象公共組件并在任意應用頁面中重用它們。
來看下面的布局組件:
// src/layouts/Main.astro
---
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
{/** 注意,下面使用了 <slot/> 標簽 **/}
<slot />
</body>
</html>
可以在任何頁面中使用此組件:
---
import Main from '../layouts/Main.astro';
---
{/** 像渲染HTML元素一樣渲染 Main 組件:<Main></Main> **/}
<Main title="Home page.">
<main>
<h1> Hello world, again </h1>
<main>
</Main>
注意這里是如何使用布局組件的。在頁面組件的 HTML 部分中,渲染了布局組件,并將頁面的元素作為子元素傳遞給了<Main>組件。
可以通過在<Main>組件中添加一個<slot/>來渲染這個內容。簡而言之,每個傳遞給<Main>布局組件的子元素都將被渲染到<Main>中的<slot/>中:
還可以通過提供 props 來提高 Astro 組件的可重用性。例如:
<Main title="Hello world" />
<Main title="Another title" />
<Main title="Hello again" />
上面的例子就是在布局組件渲染時將不同的標題傳遞給它:
// src/layouts/Main.astro
---
const { title } = Astro.props;
---
<h1>{title} </h1>
主題和模板
Astro 在過去幾年中的增長很大程度上歸功于它的易于采用和開發。Astro 通過提供多樣化的模板,為工開發者快速啟動項目并編寫更少的代碼提供了極大的便利。
中間件
中間件在幫助 Astro 轉變為成熟的 Web 框架的過程中發揮了重要作用。
大多數全棧 Web 框架都有中間件實現,例如 NestJS。中間件位于客戶端請求和服務器應用邏輯的其余部分之間,起到中心化邏輯的作用,例如身份驗證、日志記錄、特性標志等。
Astro 也提供了中間件,下面是 Astro 中基本中間件的結構:
// src/middleware.js|ts
import { defineMiddleware } from "astro/middleware";
const middleware = defineMiddleware((context, next) => {
// 在這里對請求進行一些操作
return next() // 不進行任何操作,原樣轉發請求
});
export const onRequest = middleware;
為了類型安全,可以從 Astro 包中導入 MiddlewareResponseHandler 類型以及 defineMiddleware 實用程序。然后,通過 defineMiddleware 實用程序定義中間件變量:
const middleware = definedMiddleware(...)
最后,導出一個指向middleware的onRequest函數。
export const onRequest = defineMiddleware(...)
Markdown 和 Content Collections API
假設你正在構建一個大型的內容驅動應用,這樣的項目預計會使用大量的Markdown、MDX、JSON或YAML文件。
組織項目內容的最佳實踐是將內容數據保存在數據庫中,可以在數據庫中驗證文檔結構并確保所需的內容符合我們想要的數據模型。
使用此解決方案時,可以將它們表示為存儲在具有預定義架構的數據庫中的數據集合:
在大多數靜態站點生成器中,驗證本地模式是很困難的。Astro 通過其 Content Collections API (內容集合)提供了強類型安全性,改變了這種情況。
內容集合是 Astro 項目的 src/content 文件夾中的任何頂級目錄:
集合中的單個文檔被稱為集合項:
以這種方式組織大型內容驅動的網站的好處在于,可以利用Astro的類型安全性來查詢和處理內容集合。例如,可以為內容集合引入一個模式,如下所示:
// src/content/config.ts
// 從 astro:content 導入工具
import { z, defineCollection } from "astro:content";
// 定義一個或多個集合的類型和模式
const blogCollection = defineCollection({
type: 'content',
// 一個包含字符串類型的對象 - 標題(title)、年份(year)、月份(month)和日期(day)
schema: z.object({
title: z.string(),
year: z.string(),
month: z.string(),
day: z.string(),
}),
});
// 導出一個單獨的 collections 對象以注冊集合。
// 鍵應該與“src/content”中的集合目錄名稱匹配。
export const collections = {
blog: blogCollection, // add the blog collection
};
現在,src/content/blog 集合中的每一項都必須遵守此結構。
下面來驗證一下每個 Markdown 的標題。例如,如果有以下內容,就會收到 TypeScript 錯誤,因為不滿足我們定義的結構:
<!-- src/content/blog/initial-blog.md -->
---
title="Hello World"
<!-- 缺少年份(year)、月份(month)和日期(day)的模式定義 -->
---
# 前端充電寶
hello world!
無論 Astro 項目有多大,內容集合都是組織內容文檔、驗證文檔結構以及在查詢或操作內容集合時享受開箱即用的 TypeScript 支持的最佳方式。
視圖轉換 API
清晰且炫酷的頁面過渡不僅可以增強用戶體驗,還可以建立流動方向并突出展示了不同頁面之間元素的關系。這就是 View Transitions API 發揮作用的地方。
Astro的 View Transitions 是一組新的API,旨在使用 View Transitions 瀏覽器 API 原生地操縱頁面過渡。Astro 是第一個將 View Transitions 引入主流的重要 Web 框架。
要開始在 Astro 項目中使用 View Transitions API,只需導入 ViewTransitions。然后,在希望提供頁面轉換的源頁面和目標頁面的頭部渲染組件:
// some-page.astro
---
import { ViewTransitions } from "astro:transitions";
---
<head>
<ViewTransitions />
</head>
// ...
ViewTransitions 負責將客戶端腳本添加到原始頁面,以攔截對其他頁面的點擊。
Astro 使用場景
說完了 Astro 的核心功能,下面來看看 Astro 的一些常見用例,如果需要開發內容驅動的網站,低延遲是很重要的,Astro 就是一個很好的選擇。
博客、作品集
如果你的個人博客或作品集是一個靜態網站,那么使用 Astro 是一個明智的選擇。除了提高網站性能外,Astro 的組件化架構還允許你在適當的位置輕松地添加動態功能。
還可以集成 Markdown 以獲得更好的創作體驗,并利用 Astro 的服務端渲染來保持 seo 友好。簡而言之,如果要在 2024 年構建靜態網站,Astro 是你的最佳選擇。
文檔站點
文檔站點通常具有大量內容,Astro 在這方面表現出色。Astro 還有一個名為 Starlight 的專用文檔框架,可以快速啟動和運行。如果需要一個干凈、直觀的文檔網站,可以快速加載以提供所需的信息,那么 Astro 是一個不錯的選擇。
Web 應用
Astro 不僅限于靜態網站。理論上,它還可以為動態 Web 應用提供動力。
然而,在這方面需要小心。盡管可以輕松地使用React、Vue、Svelte等選項開發完整的Web應用,但只有在確實有意義的情況下才應考慮使用 Astro。
對于是否在全棧應用中使用 Astro,應該考慮:能否利用islands來提升應用的性能?換句話說,是否想要構建一個大部分是靜態的、具有按需加載的islands的多頁面應用?
如果答案是肯定的,可以嘗試使用 Astro。
Astro vs Next.js
Astro 和 Next.js 都是現代 JavaScript 框架,旨在創建高性能的應用。然而,Astro 最初是作為靜態站點生成器開發的,而 Next 最初是作為構建具有狀態管理功能的豐富應用的框架:
盡管 Astro 仍然是靜態網站的絕佳選擇,但它也努力縮小差距,以創建更多有狀態的應用。然而,即使可以在 Astro 中渲染完整的 React 客戶端應用,這并不一定意味著你應該這樣做。
如果你的網站大部分是靜態的并且性能是優先考慮的,可以考慮使用 Astro。如果正在構建一個功能豐富、有狀態的應用,Next 可能是更好的選擇。
值得注意的是,靜態和有狀態之間的區別可能并不總是很清楚。完全可以擁有一個混合了兩者的應用。最終,框架的選擇取決于具體用例和業務要求。