如何使用 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 的配置文件,用于自定义 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/lume_theme_simple_blog@v0.2.1/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://: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>
`;
}
使用 TypeScript 在 Lume 中构建页面很简单,我们只需要 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 或 LightningCSS。
为了简化,本演示中我们将使用出色的 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/std@0.167.0/http/file_server.ts
root: ./_site
将更改推送到 GitHub,您的站点将在几秒钟内构建并上传到 Deno Deploy。
点击蓝色的查看按钮,您将在 *.deno.dev
(如果您添加了自定义域名,则为不同域名)上看到您的新博客。
就是这样。这很容易,不是吗?请查看 Lume 文档站点,了解更多关于这个静态站点生成器的信息,并在 展示部分 查看真实示例。
卡住了?欢迎到 Deno 的 Discord 打个招呼!