跳至主要内容
Deno 2 终于发布了 🎉️
了解更多
Astro websites run great on Deno

使用 Deno 和 Deno Deploy 构建和发布 Astro 网站

Astro 是一个为内容中心网站设计的 Web 框架。其 API 设计和工具使得逐步构建更复杂的网站变得轻而易举,但默认情况下不会向客户端发送任何 JavaScript 代码。就像 鸡肉华夫饼 一样,Deno 和 Astro 已经很好地协同工作了 一段时间。但随着最近 Deno 运行时的 1.35 版本发布,使用 Deno 与 Astro 的体验变得更加出色。

今天,我们将介绍使用 Astro 和 Deno 协同工作的所有方法,然后使用 Deno Deploy 在生产环境中运行您的应用程序。如果您愿意,还可以查看这个 YouTube 上的实时代码示例,其中我介绍了本文中大部分代码和概念。

您准备好尝试自 辣蜂蜜 和意大利辣香肠披萨组合以来最棒的组合吗?如果答案是肯定的,我希望您能继续阅读本文。

开始之前

要使用 Astro 和 Deno 构建静态或服务器端渲染的网站,您需要安装一些工具。

您需要… 因为…
我们将使用 Deno CLI 在 Deno 运行时本地运行您的 Astro 网站。
一个 npm 客户端 您还需要一个与 npm 兼容的客户端,例如 原始的 npmyarnpnpm。由于 Astro 是为 Node.js 和 npm 设计的,因此您需要通过 package.json 管理依赖项,并使用 Astro 文档中描述的基于 npm 的命令。

如果您没有安装 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 协同使用。

我们将介绍如何执行这两项操作,但首先让我们从构建一个简单的静态网站并将其托管在 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 演示应用程序看起来像这样,默认情况下在 https://127.0.0.1:3000 上运行。

default astro website layout

要构建用于生产的静态网站,请使用 npm run build 命令。这将在当前目录中创建一个 dist 文件夹,其中包含运行您的应用程序所需的所有 HTML、CSS 和(最终)JavaScript 代码。此默认应用程序将足以让我们了解如何使用 Deno Deploy 将此静态网站发布到互联网上。

接下来,让我们从 GitHub 设置自动部署。首先将您刚刚生成的 Astro 项目推送到一个公共或私有的 GitHub 仓库。如果您不熟悉如何使用 GitHub 执行此操作,请 参阅此处提供的文档

将您的 Astro 网站上传到 GitHub 仓库后,注册 Deno Deploy 并导航到您的 项目仪表板。单击“新建项目”按钮,然后选择部署现有的 GitHub 仓库,如下所示。

create a new Deploy project from an existing GitHub repo

Deno Deploy 应该会很有帮助地检测到您正在尝试部署使用 Astro 构建的静态网站。要设置自动部署,您需要配置一个 GitHub Action,以便在您将新代码推送到仓库的 main 分支时执行必要的构建步骤。单击提示您执行此操作的按钮。

continue to set up auto deploys

在 GitHub 上,您可以设置一个 工作流配置文件,该文件将在您每次推送到 main 分支时执行构建任务。除非您更改了 Astro 构建静态网站资源的方式和位置,否则您应该可以使用此文件而不进行更改 - Deno Deploy 将为您注入新的项目名称。一旦您对该配置感到满意,您可以通过单击屏幕右上角的绿色“提交更改”按钮(如果看不到它,请向上滚动页面)直接将它提交到您的仓库。

edit and commit the config file

🤔  上面的配置中“file_server.ts”行是怎么回事?Deno 标准库 提供了一个实用程序,可以从文件夹中提供静态资源。Deno Deploy 默认使用它来提供 Astro 网站中的静态资源。如果您想设置自己的静态文件服务器,请随意!您可以学习如何在 这篇文章 中执行此操作。

提交对该文件的更改后,应该会触发 GitHub 仓库中的新构建。此后不久,您的 Deno Deploy 仪表板应该会更新为最新的代码,这些代码是从 GitHub 构建和部署的。

your project dashboard

干得好!您刚刚使用 Astro 构建并部署了一个静态网站,并在 Deno Deploy 的帮助下将其发布到互联网上。虽然这很令人兴奋,但许多现代 Web 应用程序仅靠静态 HTML 无法运行。要动态生成我们网站中的页面,我们需要在服务器上编写一些逻辑。而这正是 Deno 真正开始为您发挥作用的地方,尤其是在您使用我们内置的数据库 Deno KV 时。

接下来,让我们看看如何使用服务器端渲染构建 Astro 网站。

使用服务器端渲染构建动态网站

虽然 Astro 的默认配置是用于生成静态网站,但它在 在服务器上渲染部分或所有页面 方面也很出色。为了实现这一点,Astro 为许多流行的托管服务提供了适配器,其中包括 Deno Deploy。Astro 网站使用 SSR 不会只生成在浏览器中运行的静态 HTML、CSS 和 JavaScript,而是在构建过程中还会生成服务器端代码。此代码将在提供页面之前运行,并允许您动态生成发送到浏览器的内容。

要开始使用 Astro 和 Deno 构建动态 Web 应用程序,我们建议您使用此模板应用程序。它预先配置了您进行 Deno 和 Astro 的 SSR 所需的所有更改,以及一些 CRUD 功能,这些功能展示了如何在 Astro 中构建数据驱动的 Web 应用程序。您可以使用此模板在终端中使用以下命令生成新项目…

npm create astro@latest -- --template denoland/deno-astro-template

…但由于我们将演示如何从 GitHub 自动部署此应用程序,您可以通过以下方式节省几个步骤 直接在 GitHub 网站上从此模板创建一个新仓库。单击下面显示的按钮,然后按照提示设置您自己的应用程序版本。

use template to bootstrap SSR app

在深入了解此应用程序的工作原理之前,让我们将其部署到 Deno Deploy 并看看它的效果。回到 Deno 项目仪表板,再次选择创建一个新应用程序并从现有的 GitHub 仓库进行部署。选择您刚刚创建的项目后,Deno Deploy 将这次检测到您正在构建一个使用 SSR 的 Astro 应用程序。

deploy detects Astro SSR

与之前一样,您将被提示设置一个 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 仓库后,您的网站将构建,您将在几分钟内看到它的运行情况。模板应用程序看起来像这样。

Deno + Astro demo app

它看起来与我们之前使用 Astro 部署的默认静态网站非常相似,只是它允许您使用页面顶部的表单添加链接卡片。您还可以通过单击每个卡片右上角的 X 来删除卡片。

此时,您可以 克隆您版本的模板项目 并查看代码。克隆项目后,使用以下命令安装所需的依赖项

npm install

然后,您可以像以前一样在您的计算机上运行应用程序,使用

npm start

但是,此应用程序还有更多活动部件 - 让我们看看一些更重要的功能更改。

为 SSR 配置 Astro

为了让 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)。

index.astro 中的组件脚本

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 来删除链接卡片,而无需刷新整个页面。

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 端点,它处理上面 JavaScript 代码中使用的异步 DELETE 请求。

resources.json.ts 中的 API 路由

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 的强大功能相结合,是一个杀手锏组合,我鼓励您更深入地探索。

您是否将 Astro 和 Deno 结合在一起使用?请务必告诉我,或者加入 Discord,让我们知道您正在构建什么!