使用 Deno 和 Deno Deploy 构建和发布 Astro 站点
Astro是一个为以内容为中心的网站设计的Web框架。它的 API 设计和工具使得逐步构建更复杂的网站变得容易,同时在默认情况下不会向客户端发送 JavaScript。就像鸡肉和华夫饼一样,Deno和Astro合作了一段时间。但是随着最近 Deno 运行时的 1.35 版本,Astro 与 Deno 一起使用的体验变得更好了。
今天,我们将介绍结合使用 Astro 和 Deno 的所有方法,然后使用 Deno Deploy 在生产环境中运行您的应用程序。
开始之前
要使用 Astro 和 Deno 构建静态或服务器渲染的网站,您需要安装一些工具。
你需要。。。 | 因为。。。 |
---|---|
Deno CLI | 我们将使用 Deno CLI 在 Deno 运行时本地运行您的 Astro 站点。 |
npm 客户端 | 你还需要一个与 npm 兼容的客户端,比如 npm、yarn 或 pnpm。由于 Astro 是为 Node.js 和 npm 设计的,因此您需要通过 package.json 来管理依赖项,并使用基于 npm 的命令,如 Astro 文档中所述。如果你没有安装 Node 和 npm,我建议安装 pnpm。它独立工作,并且使用磁盘空间既快速又高效。 |
为什么我同时需要 Deno 和 npm? 虽然你可以只用 npm(和Node.js) 来构建Astro网站,但有几个原因说明为什么使用Deno运行时来构建和运行你的Astro网站是有用的。
- Deno 运行时功能:Deno 运行时支持服务器上的高级 JavaScript 和 TypeScript 语法。它还具有使开发更容易的内置功能,例如Deno KV,这是一个键值数据库,无需额外配置即可在本地和Deno Deploy中工作。
- 在生产中使用Deno Deploy:Deno Deploy是一个全球无服务器JavaScript平台,具有超快的部署时间,由V8隔离而不是虚拟机提供支持。这是在靠近用户的位置运行应用程序代码的好方法,可为他们提供尽可能快的加载时间。
一旦安装了 Deno 和 npm 客户端,我们可以尝试以两种方式同时使用 Astro 和 Deno。
- 生成和部署静态站点。
- 使用 Astro + Deno 构建更动态的服务器渲染应用程序。
我们将介绍如何同时执行这两项操作,但让我们从构建一个简单的静态站点并将其托管在Deno Deploy上开始。
构建和部署静态站点
默认情况下,Astro配置为为您的网站生成静态HTML和CSS。在此模式下,您可以在任何可以托管这些文件的环境中为您的网站提供服务,包括 Deno Deploy。让我们看看这将如何工作。首先在终端中使用以下命令生成默认的 Astro 项目。
npm create astro@latest
这将启动一个交互式提示,您可以在其中配置 Astro 项目。出于我们的目的,您可以接受所有默认配置选项。这个过程最终会看起来像这样。
kevin@kevin-deno astro-demo % npm create astro@latest
╭─────╮ Houston:
│ ◠ ◡ ◠ Let's build something fast!
╰─────╯
astro v2.8.5 Launch sequence initiated.
dir Where should we create your new project?
./extraterrestrial-equator
tmpl How would you like to start your new project?
Include sample files
✔ Template copied
deps Install dependencies?
Yes
✔ Dependencies installed
ts Do you plan to write TypeScript?
Yes
use How strict should TypeScript be?
Strict
✔ TypeScript customized
git Initialize a new git repository?
Yes
✔ Git initialized
next Liftoff confirmed. Explore your project!
Enter your project directory using cd ./extraterrestrial-equator
Run npm run dev to start the dev server. CTRL+C to stop.
Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
╭─────╮ Houston:
│ ◠ ◡ ◠ Good luck out there, astronaut! 🚀
╰─────╯
kevin@kevin-deno astro-demo %
生成新项目后,您可以按照指示输入新项目的文件夹(在上面的示例中), cd ./extraterrestrial-equator
并使用 npm start
测试新应用程序。基本的 Astro 演示应用程序看起来像这样,默认情况下在 http://localhost:3000 上运行。
若要为生产构建静态站点,请使用 npm run build
命令。这将在当前目录中创建一个 dist
文件夹,其中包含运行应用程序所需的所有 HTML、CSS 和(最终)JavaScript 代码。这个默认应用程序足以让我们了解如何使用 Deno Deploy 在 Internet 上发布此静态站点。
接下来,让我们从 GitHub 设置自动部署。首先将您刚刚生成的 Astro 项目推送到公共或私有 GitHub 存储库。如果您不熟悉使用 GitHub 执行此操作,请参阅此处的文档。
一旦您的 Astro 站点上传到 GitHub 存储库,请注册 Deno Deploy 并导航到您的项目仪表板。单击“新建项目”按钮,然后选择部署现有的 GitHub 存储库,如下所示。
Deno Deploy 应该有助于检测到您正在尝试部署使用 Astro 构建的静态站点。但是,若要设置自动部署,需要配置 GitHub 操作,以便在将新代码推送到 main
存储库分支时执行必要的生成步骤。单击提示您下一步执行此操作的按钮。
在 GitHub 上,您可以设置一个工作流配置文件,该文件将在您每次推送到 main
分支时执行构建任务。除非您更改了有关 Astro 构建静态站点资产的方式和位置,否则您应该能够原封不动地使用此文件 - Deno Deploy 将为您注入新的项目名称。对此配置感到满意后,可以通过单击屏幕右上角的绿色“提交更改”按钮,从此 UI 直接将其提交到存储库(如果没有看到,请一直向上滚动页面)。
上面配置中的“file_server.ts”行是怎么回事?
Deno 标准库提供了一个实用程序,该实用程序将提供文件夹中的静态资产。Deno Deploy 默认使用它来提供 Astro 站点中的静态资产。如果您想设置自己的静态文件服务器,请随意!您可以在这篇博文中了解如何执行此操作。
提交对此文件的更改后,应在 GitHub 存储库中触发新的生成。不久之后,您的 Deno Deploy 仪表板应使用从 GitHub 构建和部署的最新代码进行更新。
干得好!您刚刚使用 Astro 构建并部署了一个静态站点,并在 Deno Deploy 的帮助下将其放在 Internet 上。尽管这令人兴奋,但许多现代 Web 应用程序无法仅靠静态 HTML 生存。要在我们的网站中动态生成页面,我们需要开始在服务器上编写一些逻辑。这就是Deno真正开始为您做一些工作的地方,特别是当您使用我们的内置数据库Deno KV时。
让我们看看接下来如何构建一个带有服务器端渲染的 Astro 站点。
使用服务器端呈现构建动态网站
虽然 Astro 的默认配置用于生成静态站点,但它也非常适合在服务器上呈现部分或全部页面。为了实现这一目标,Astro为许多流行的托管服务提供了适配器,其中包括Deno Deploy。除了生成在浏览器中运行的静态HTML,CSS和JavaScript之外,使用SSR的Astro站点还将在构建过程中生成服务器端代码。此代码将在您的页面投放之前运行,并允许您动态生成发送到浏览器的内容。
要开始使用 Astro 和 Deno 的动态 Web 应用程序,我们建议使用此模板应用程序。它预配置了使用 Deno 和 Astro 进行 SSR 所需的所有更改,以及少量的 CRUD 功能,展示了如何在 Astro 中构建数据驱动的 Web 应用程序。您可以使用此模板通过此命令在终端中生成一个新项目...
npm create astro@latest -- --template denoland/deno-astro-template
...但由于我们将演示如何从 GitHub 自动部署此应用,因此可以通过直接从 GitHub 网站上从此模板创建新存储库来节省一些步骤。单击下面显示的按钮,然后按照提示设置您自己的此应用程序版本。
在深入研究这个应用程序是如何工作的之前,让我们把它送到Deno Deploy,看看它有什么作用。回到 Deno 项目仪表板,再次选择创建新应用程序并从现有 GitHub 存储库进行部署。选择您刚刚创建的项目后,Deno Deploy 这次将检测到您正在构建使用 SSR 的 Astro 应用程序。
与以前一样,系统将提示您设置一个 build.yml
文件,该文件将在每次将更改推送到 main
分支时部署站点。但是,这次的配置会略有不同,因为我们将使用 Astro 生成的 Deno 脚本作为应用程序的入口点。
- name: Upload to Deno Deploy
uses: denoland/deployctl@v1
with:
project: "empty-hedgehog-41"
entrypoint: "server/entry.mjs" # This file is generated by "npm build"
root: "dist" # SSR apps still output to the "dist" folder by default
将此文件提交到 GitHub 存储库后,将生成站点,并且你将能够在几分钟内看到它的实时状态。模板应用程序将如下所示。
它看起来与我们之前使用 Astro 部署的默认静态站点非常相似,只是它使您能够使用页面顶部的表单添加链接卡。您也可以通过单击每张卡右上角的 来 X
删除卡片。
此时,您可以克隆模板项目的版本并稍微浏览一下代码。克隆项目后,使用以下命令安装所需的依赖项:
npm install
然后,您可以像以前一样在计算机上运行该应用程序,如下所示:
npm start
不过,此应用程序还有一些活动部件 - 让我们看看一些更重要的功能更改。
为 Astro 配置 SSR
为了让 Astro 知道它应该更喜欢在服务器上渲染页面,您需要进行一些配置更改。在我们的模板项目中,这个文件被调用 astro.config.js
,而不是 astro.config.mjs
像在 Astro 文档中那样 - 默认情况下,Deno 中的所有 JavaScript 文件都是 ESM 模块。以下是您将在此文件中找到的内容。
import { defineConfig } from "astro/config";
// import deno from "@astrojs/deno";
import deno from "deno-astro-adapter";
// https://astro.build/config
export default defineConfig({
output: "server",
adapter: deno(),
});
上面,我们将 output
模式设置为 server
,这告诉 Astro 我们希望默认为每个请求动态呈现页面,除非我们在给定页面上有不同的说法。我们还配置了一个 Deno adapter
,这有助于生成服务器端代码,这些代码将随着对我们网站的每个请求而运行,并动态生成响应。
Astro 团队为 Deno 维护了一个适配器,但在撰写本文时(2023 年 7 月),有一个小问题悬而未决,即使用现代语言功能的有效 Deno 代码无法与其适配器配合使用。上面的代码使用修补版本,这应该不需要太长时间。
在 Deno 中运行开发服务器
将Deno与Astro一起使用的主要原因之一是您的服务器端代码可以利用Deno运行时的功能。为了以这种方式有效地测试我们的代码,我们需要使用 Deno 运行我们的本地开发服务器。我们处理这个问题的方法是替换一些在 中 package.json
配置的 npm 脚本,并让它们使用 Deno 而不是 Node 执行相同的任务。
{
"name": "deno-astro-template",
"type": "module",
"version": "1.0.0",
"scripts": {
"dev": "deno run -A --unstable npm:astro dev",
"start": "deno run -A --unstable npm:astro dev",
"build": "astro build",
"preview": "deno run -A --unstable ./dist/server/entry.mjs",
"astro": "astro",
"format": "deno fmt && prettier --write ."
}
}
运行代码以在服务器上生成页面
在 Astro 组件中, .astro
文件顶部的受保护的“前言”代码(称为“组件脚本”)将在构建时为静态生成的页面运行,或者在服务器呈现页面的每个请求上运行。此模板修改组件 index.astro
脚本,使其既具有允许用户提交新链接卡内容的表单,也具有将在每个请求中获取页面新内容的数据查询。
此页面上的表单还将向同一 URL 发送 POST
请求,这将导致此代码在响应表单提交时做出不同的响应(保存新资源,然后重定向到 GET
)。
索引中的组件脚本
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";
import { addResource, listResources, Resource } from "../data/resources";
// Process form submission if required
if (Astro.request.method === "POST") {
try {
const data = await Astro.request.formData();
const resource: Resource = {
url: data.get("url")?.toString() || "",
title: data.get("title")?.toString() || "",
summary: data.get("summary")?.toString() || "",
};
await addResource(resource);
} catch (error) {
console.error(error);
}
// Redirect to home page to avoid duplicate form submissions
return Astro.redirect("/");
}
// Get a list of resources
const resources: Resource[] = await listResources();
用于删除链接卡的客户端 JavaScript
此模板还使用在客户端上运行的 JavaScript 来删除链接卡,而无需刷新整个页面。
Card.astro 中的客户端脚本
document.querySelectorAll('span.delete').forEach((span) => {
span.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
const title = (span as HTMLElement).dataset.title || '';
const encTitle = encodeURIComponent(title);
const url = `/api/resources.json?title=${encTitle}`;
try {
const res = await fetch(url, { method: 'DELETE' });
if (res.ok) {
span.parentElement?.remove();
}
} catch (err) {
console.log(err);
}
});
});
用于处理删除链接卡的 API 端点
此模板也有一个 API 端点,用于处理上述 JavaScript 代码中使用的异步 DELETE
请求。
resources.json.ts 中的 API route
import { APIRoute } from "astro";
import { deleteResource } from "../../data/resources.ts";
export const del: APIRoute = async ({ request }) => {
const title = new URL(request.url).searchParams.get("title");
if (!title) return new Response(null, { status: 400 });
await deleteResource(title);
return new Response(null, { status: 204 });
};
切换到Deno KV
默认情况下,模板项目会将数据保存到内存中的 Map 对象。但是通过一些小的更改,模板应用程序可以配置为将数据存储在Deno KV中。此代码使用 Deno KV 公开内存中数据存储使用的相同 API,但使这些更改在 Deno KV 中持久化。
resources_kv.ts 中的 Deno KV 数据访问
const db = await Deno.openKv();
export interface Resource {
url: string;
title: string;
summary: string;
}
export async function addResource(resource: Resource) {
return await db.set(["resources", resource.title], resource);
}
export async function listResources(): Promise<Resource[]> {
const iter = db.list({ prefix: ["resources"] });
const resources = [];
for await (const res of iter) resources.push(res.value as Resource);
return resources;
}
export async function deleteResource(title: string) {
return await db.delete(["resources", title]);
}
请注意,为了在Deno Deploy上使用Deno KV,您需要成为私人测试版的一部分。但不久之后,Deno KV 将可供所有 Deploy 用户使用,因此如果您在发布几周后阅读本文,那么您很有可能能够立即在 Deploy 上使用 KV :)
Deno 和 Astro
Astro作为一个Web框架有很多值得喜爱的地方。Astro 组件功能强大且灵活,无需太多帮助,但也可以与您喜欢的 UI 框架一起使用。选择静态站点或服务器呈现的页面(或两者的某种组合)使得使用 Astro 提供完整的动态网站成为可能。将这种出色的 API 设计与 Deno 运行时和 Deno Deploy 的强大功能相结合是一个杀手级组合,我鼓励您进一步深入探索。