如何使用 Lume 构建静态网站
这是一篇由 Óscar Otero(Lume 的创建者)撰写的客座博客文章。
Deno 是创建动态 Web 应用程序和 API 的绝佳选择,尤其是在与 Deno Deploy 一起使用时,可以在边缘生成响应。
但是对于不需要服务器动态响应的用例,例如博客、文档和登录页面,静态网站(一个预先构建的网站,包含要交付给浏览器的确切 HTML、CSS 和 JavaScript 文件)是一个更好且更有效率的替代方案。
在本教程中,我们将学习如何使用 Lume(发音为 /lume/
)创建静态网站,它是一个由 Deno 编写的快速、极其灵活、可组合且可扩展的静态网站生成器。
设置
设置 Lume 项目的推荐方法是运行命令
deno run -Ar --unstable https://deno.land/x/lume/init.ts
经过几个问题后,您将在工作目录中看到 3 个新文件
_config.ts
:用于自定义 Lume 的配置文件。deno.json
:带有某些用于运行 Lume 的有用任务的 Deno 配置文件。import_map.json
:Deno 用于解析裸规范的导入映射文件。
此外,如果您使用的是 VSCode,强烈建议您 使用 Deno 扩展。(了解有关使用 VSCode 配置 Deno 的信息。)
构建博客:简单方法
博客是静态网站生成器和 Lume 最常见的用例示例,当然,Lume 可以为您构建一个。如果您没有寻找过于复杂的东西,那么 “简单博客”主题 可能就足够了。您只需要在 _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;
然后,使用 /posts/
文件夹中的 markdown + 前端 matter 格式保存您的帖子。例如
---
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
文件作为布局。请注意,布局可以访问帖子的前端 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 文件没有前端 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 Actions 模式。这是因为 Deno Deploy 没有 CI 来构建网站,所以我们需要使用 GitHub Actions 工作流程来构建静态网站并将其上传到 Deno Deploy。
然后点击链接按钮,您将看到以下屏幕。
Deno Deploy 已生成配置 GitHub Actions 工作流程所需的代码。复制此代码并将其保存到 .github/workflows/deploy.yml
中。
步骤 2:配置 GitHub Actions
我们刚刚创建的 GitHub Actions 工作流程文件需要一些调整,这些调整以 #TODO
注释标记。
- 设置构建步骤。在我们的例子中,我们需要设置 Deno 并运行
deno task build
。 - 更新入口点以使用 deno_std 的
file_server.ts
,并将root
设置为./_site
。
请注意,如果您想使用 Lume 的中间件(例如 expires
用于缓存或 not_found
用于显示自定义 404 页面),您需要创建一个新的 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 中打个招呼吧!