如何使用 Lume 构建静态站点
这是一篇客座博客,由 Lume 的创建者 Óscar Otero 撰写。
Deno 是创建动态 Web 应用程序和 API 的绝佳选择,尤其是与 Deno Deploy 结合使用以在边缘生成响应时。
但是对于不需要服务器动态响应的用例,例如博客、文档和落地页,静态站点(预构建的站点,包含要交付给浏览器的确切 HTML、CSS 和 JavaScript 文件)是更好且性能更高的替代方案。
在本教程中,我们将学习如何使用 Lume(发音为 /lume/
)创建一个静态站点,Lume 是一个快速、极其灵活、可组合和可扩展的静态站点生成器,用 Deno 编写。
设置
设置 Lume 项目的推荐方法是运行命令
deno run -Ar --unstable https://deno.land/x/lume/init.ts
在回答几个问题后,您将在工作目录中看到 3 个新文件
_config.ts
:用于自定义 Lume 的配置文件。deno.json
:Deno 配置文件,包含一些用于运行 Lume 的实用任务。import_map.json
:Deno 使用的导入映射文件,用于解析裸标识符。
此外,强烈建议如果您使用 VSCode,则使用 Deno 扩展。(了解有关使用 VSCode 配置 Deno 的信息。)
构建博客:简单方法
博客是静态站点生成器最常见的用例示例,Lume 当然可以为您构建一个。如果您不追求过于复杂的东西,那么 “Simple blog” 主题就足够了。您只需将其导入 _config.ts
文件并使用它。
import lume from "lume";
import blog from "https://deno.land/x/[email protected]/mod.ts";
const site = lume().use(blog());
export default site;
然后使用 markdown + front matter 格式将您的帖子保存在 /posts/
文件夹中。例如
---
title: Static sites with Lume + Deno Deploy
date: 2022-11-05
author: Óscar Otero
tags:
- Deno
- Static site generators
---
Deno is always a great choice to create dynamic web applications and APIs,
especially when it's combined with Deno Deploy to generate responses at the
edge. ...
运行 deno task serve
,您将在 localhost:3000
上看到您的新博客!
但是我想自己制作一些东西
好的,明白了!与其使用现有主题,不如从头开始构建一些东西。
开始入门
Lume 不要求任何文件结构即可工作,但对于本演示,我们将所有帖子文件保存在名为 /posts
的目录中。因此,我们有以下结构
|_ posts/
| |_ my-first-post.md
| |_ my-second-post.md
|_ config.ts
|_ deno.json
|_ import_map.json
运行 deno task serve
以在 localhost:3000
上查看站点。您将看到 404 错误页面,因为索引文件尚不存在。
但是您可以看到帖子。默认情况下,帖子的 URL 根据源路径计算。例如,文件 /posts/my-first-post.md
输出 URL https://127.0.0.1:3000/posts/my-first-post/
。
布局
如您所见,该页面仅显示渲染为 HTML 的 markdown 内容。让我们创建一个布局,将此内容包装到适当的 HTML 结构中。默认情况下,布局存储在特殊的 _includes
文件夹中,因此我们需要创建此文件夹,并在其中创建文件 post.njk
,内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
</head>
<body>
<main>
<article>
<header>
<h1>{{ title }}</h1>
<p>By {{ author }}</p>
</header>
{{ content | safe }}
</article>
</main>
</body>
</html>
.njk
扩展名用于 Nunjucks,这是一种由 Mozilla 创建的流行模板语言,Lume 默认支持它。当然,您可以使用其他格式,例如 JavaScript、TypeScript 甚至 JSX、Pug、Eta 等(使用插件),但现在让我们保持简单。
我们将在 posts
文件夹中创建一个 _data.yml
文件,内容如下
layout: post.njk
type: post
此文件将这两个变量分配给此目录中的所有帖子。
现在让我们忽略 type
变量。
layout
变量是一个特殊值,包含用于渲染页面的布局的文件名。这意味着此目录中的所有页面都将使用 _includes/post.njk
文件作为布局。请注意,布局可以访问帖子 front matter 的值,例如 title
或 author
。
我们的帖子现在看起来好一点了!
列出所有帖子
假设我们想在主页中显示所有帖子的列表。对于此示例,我将在 TypeScript 中创建主页,因此我们需要在根目录中创建 index.tmpl.ts
文件,代码如下
import type { PageData } from "lume/core.ts";
export default function ({ search }: PageData) {
const posts = search.pages("type=post");
return `
<h2>Posts</h2>
<ul>
${
posts.map((post) =>
`<li><a href="${post.data.url}">${post.data.title}</a></li>`
).join("")
}
</ul>
`;
}
在 Lume 中使用 TypeScript 构建页面很容易,我们只需要 export default
一个函数,该函数将页面内容作为 string
返回。
此函数接收页面上下文数据作为第一个参数,包括一些有趣的助手函数,例如 search
,我们可以使用它来查询并返回所有带有 type=post
变量的页面。(还记得我们之前在 posts/_data.yml
文件中插入的 type
值吗?它只是一个标志,用于在此处轻松选择这些页面。)
我们的主页看起来像这样
我可以在页面中包含完整的 HTML 代码,但我喜欢布局,因此我创建了以下 _includes/homepage.njk
布局文件
---
title: The Óscar's blog
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
</head>
<body>
<main>
<header>
<h1>{{ title }}</h1>
</header>
{{ content | safe }}
</main>
</body>
</html>
现在我们必须配置 index.tmpl.ts
文件以使用此布局。TypeScript 文件没有 front matter 来存储数据,它们只是将值作为命名导出公开
import type { PageData } from "lume/core.ts";
export const layout = "homepage.njk";
export default function ({ search }: PageData) {
const posts = search.pages("type=post");
return `
<h2>Posts</h2>
<ul>
${
posts.map((post) =>
`<li><a href="${post.data.url}">${post.data.title}</a></li>`
).join("")
}
</ul>
`;
}
主页现在看起来好一点了
添加样式
Lume 有许多插件可用于 JavaScript,以支持 JSX、MDX 等格式。出于样式目的,有一些可用的插件可用于 SASS、PostCSS、WindiCSS 或 LighningCSS。
为简单起见,在本演示中,我们将使用出色的 missing.css(顺便说一句,文档站点也是用 Lume 构建的)。此 css 文件提供了一些不错的开箱即用样式,无需修改我们的 HTML。为此,只需在我们刚刚创建的两个布局中包含 <link rel="stylesheet" href="https://the.missing.style/">
行。
准备部署
静态站点的众多优点之一是托管。您可以将您的站点托管在任何地方,几乎没有服务器要求。
在本演示中,我们将使用 Deno Deploy 来提供站点服务。由于其设计,所有传入的请求都由 JavaScript 文件处理,因此我们可以自定义站点的文件交付方式。
步骤 1:在 Deno Deploy 中创建一个项目
转到 Deno Deploy 并创建一个新项目。您将看到如下屏幕
选择分支时,重要的一点是使用 GitHub Action 模式。这是因为 Deno Deploy 没有 CI 来构建站点,因此我们需要使用 GitHub Actions 工作流程来构建静态站点并将其上传到 Deno Deploy。
然后单击链接按钮,您将看到以下屏幕
Deno Deploy 已生成配置 GitHub Actions 工作流程所需的代码。复制此代码并将其保存到 .github/workflows/deploy.yml
中。
步骤 2:配置 GitHub Actions
我们刚刚创建的 GitHub Action 工作流程文件需要进行一些调整,标记为 #TODO
注释
- 设置构建步骤。在我们的例子中,我们需要设置 Deno 并运行
deno task build
。 - 更新入口点以使用 deno_std 的
file_server.ts
,并将root
设置为./_site
。
请注意,如果您想使用 Lume 的中间件,例如用于缓存的 expires
或用于显示自定义 404 页面的 not_found
,则需要创建一个新的 serve.ts
,添加您的中间件,并将其设置为 Deno Deploy 的 entrypoint
。
经过这些修改后,这是最终版本
name: Deploy
on: [push]
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
permissions:
id-token: write # Needed for auth with Deno Deploy
contents: read # Needed to clone the repository
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Setup Deno environment
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Build site
run: deno task build
- name: Upload to Deno Deploy
uses: denoland/deployctl@v1
with:
project: oscarotero-lume-blog-demo
entrypoint: https://deno.land/[email protected]/http/file_server.ts
root: ./_site
将更改推送到 GitHub,您的站点将在几秒钟内构建并上传到 Deno Deploy。
单击查看蓝色按钮以在 *.deno.dev
上查看您的新博客(如果您添加了自定义域名,则可以使用其他域名)。
就这样。这很容易,不是吗?查看 Lume 文档站点,以了解有关此静态站点生成器的更多信息,并在展示区中查看真实示例。
遇到困难?来 Deno 的 Discord 打个招呼!