前端技术日新月异,最初的静态网站逐渐被由服务端生成的网站所取代,后来又逐渐向客户端渲染的应用转变。不过客户端渲染也存在一些问题,如加载时间变长和搜索引擎优化难度等。Astro 这个新的前端框架结合了服务端渲染和客户端渲染的优点,可以更好地解决这些问题。
本文就来介绍一下这两年爆火的前端框架 Astro,它在两年的时间新增了 30k+ star:
这个前端框架,有点不一样。
Astro 是一个开源的 JavaScript 框架,用于在流行的UI框架(如React、Preact、Vue 或 Svelte)之上生成 Web 应用。Astro 的页面由多个独立的组件组成。为了提高加载速度,Astro 会在服务端对页面进行预渲染,并剥离所有 JavaScript,除非将某个组件标记为交互式,此时 Astro 将发送必要的最小量 JavaScript 以实现交互功能。
通过这种策略,Astro 页面加载速度快,因为在首次渲染时不需要执行任何 JavaScript。在注水的过程中,Astro会将 JavaScript “注入”到组件中,使它们变成动态的。
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 岛屿就是嵌入静态HTML页面中的交互式UI组件。一个页面上可以存在多个岛。每个岛都是通过独立的部分水合方式进行渲染。也就是说,每个岛都是独立水合的。
那这么做有什么好处呢?
通过利用岛屿,Astro 应用可以具有出色的初始加载时间,而不会受到 JavaScript 的限制。大部分站点保持静态状态,只有在初始页面加载之后需要时才会对交互部分或岛进行水合。
上面介绍了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:
在 Astro 中,不仅可以使用 .astro 组件作为页面,还可以使用以下方式构建页面:
上面我们讨论了页面和 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] 将生成三个博客页面:
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|tsimport { 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、MDX、JSON或YAML文件。
组织项目内容的最佳实践是将内容数据保存在数据库中,可以在数据库中验证文档结构并确保所需的内容符合我们想要的数据模型。
使用此解决方案时,可以将它们表示为存储在具有预定义架构的数据库中的数据集合:
在大多数静态站点生成器中,验证本地模式是很困难的。Astro 通过其 Content Collections API (内容集合)提供了强类型安全性,改变了这种情况。
内容集合是 Astro 项目的 src/content 文件夹中的任何顶级目录:
集合中的单个文档被称为集合项:
以这种方式组织大型内容驱动的网站的好处在于,可以利用Astro的类型安全性来查询和处理内容集合。例如,可以为内容集合引入一个模式,如下所示:
//
本文链接:http://www.28at.com/showinfo-26-31991-0.htmlAstro,这个前端框架有点不一样!
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com