[11ty] 初识 11ty

再一次体验 Web 开发狂野的那一面。

11ty (Eleventy) 是一个多模板静态网站生成器。多模板意味着它支持不止一个模板语言——实际上它支持 11 个模板语言,所以叫 11ty. 不过在整个工程中,我们只会用到 liquid, njk, jsmarkdown 四项;其中以 liquidmarkdown 两项为主。

项目结构

11ty 的项目结构是平坦的:新建一个文件夹,初始化一个空白的 NodeJS 工程,然后安装 11ty 依赖项即可:

mkdir blog
cd blog
npm init -y
npm i @11ty/eleventy

生成第一个页面

我们编写一个 hello.md:

Hello, world!

然后调用 11ty 生成站点:

npx @11ty/eleventy

11ty 会自动扫描项目内所有支持的文件,并逐个按文件夹结构生成到 _site/ 目录下,打开 _site/hello.html, 其应该类似:

<p>Hello, world!</p>

为页面指定父模板

你可能注意到了:生成结果怎么只有一行光秃秃的

? 这个东西甚至不是完整的 HTML 页面!

这实际上是符合 11ty 的设计概念的:万物皆模板,模板套模板,不自作主张插入内容。我们的 hello.md 只包含一个段落,它也没有父模板,那么生成结果自然就也只有一个段落。要搞成完整的 HTML 页面,我们需要提供一个页面模板。

新建一个文件夹 _includes/, 这个文件夹将会成为 11ty 查找父模板的目录。在 _includes/ 下新建文件 page.liquid, 我们来编写一个基本的页面模板:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
    
</body>
</html>

然后编辑 hello.md 使用此模板:

---
layout: page
---

Hello, world!

调用 11ty 编译之后,_site/hello.html 就会变成:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
    <p>Hello, world!</p>
</body>
</html>

文件头(Front matter)和元数据

虽然按道理 front matter 应该翻译成前言,但是——

对于任何一个非 .js 文件,都可以用类似 hello.md 开头的方式定义此文件所携带的元数据。即:在文件最开头通过三个减号 (---) 分隔出一片区域,通过严格 YAML 格式指定元数据。最明显的更改是对于布尔类型数据,只能使用 truefalse; yesno 会被看作字符串。

数据层叠

当通过 layout 指定父模板时,子模板定义的数据会层叠于父模板。即,如果我们这样修改页面模板 _includes/page.liquid 和内容数据 hello.md

---
language: zh
---
<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="UTF-8">
    <title>[11ty] 初识 11ty</title>
</head>
<body>
    
</body>
</html>
---
layout: page
language: en
title: Hello!
---

Hello, world!

最终会生成这样的结果:

<!DOCTYPE html>
<html lang="en"><!-- 虽然父模板定义了 language 变量,但是子模板覆盖了 -->
<head>
    <meta charset="UTF-8">
    <title>Hello!</title><!-- 虽然父模板没有定义 title, 但是子模板提供了 -->
</head>
<body>
    <p>Hello, world!</p><!-- content 是一个特殊变量,其内容为子模板的编译结果 -->
</body>
</html>

11ty 内置数据

除了文件中明确提供的数据,11ty 在编译过程中还会默认提供一些内置数据以便使用。

11ty 关键数据

一些文件头数据项属于关键数据,即能够影响 11ty 编译结果的数据。下表是一些我们关心的数据项目:

名称 类型 作用 默认值 备注
layout string 指定父模板名称 "" 父模板一定是从 _include 文件夹中读取的
permalink string | string[] 相对于 _site 的输出路径 当前文件相对于文章目录的路径 此项为数组时,则在每个路径上都输出一份副本
pagination Pagination 分页数据 {} 我们会在之后介绍分页
eleventyExcludeFromCollections bool 在自动分类中排除此页面 false 我们会在之后介绍如何使用自动分类功能

自动编译和内建 HTTP 服务器

每次修改一遍文件就得调用一遍 npx 太麻烦了,但好在 11ty 内建了文件修改监视功能。同时 11ty 还内建了一个 HTTP 服务器,这样就不需要手动配置一个 Web 服务器

npx @11ty/eleventy --serve --quiet

--serve 参数会启动 11ty 的内置监视和 HTTP 服务器功能;--quiet 参数关闭 11ty 的详细编译输出。启动后,11ty 会自动选择一个从 8080 端口开始往上的未被占用的端口作为其 HTTP 服务器的端口。

通过 JS 配置 11ty

11ty 的许多行为是可以通过特殊的 JS 文件 eleventy.config.js 控制的。这个文件放置在项目根目录下,在 11ty 执行编译动作(包含自动编译)时将会被执行。

修改 package.json

我们需要给 package.json 添加:

{
    ...
    "type": "module",
    ...
}

这主要是为了配合 11ty 使用 ES Module.

脚本格式

eleventy.config.js 需要导出一个默认函数用于更改 11ty 的配置:

export default function (eleventyConfig) {
  // 在这里写配置
}

之后的代码如果没有明确指定,都是写在函数体内的。11ty 的配置代码通常没有先后顺序,可以按需要自行调整。不过接下来的关键调整最好放在函数开头。

指定文章目录

我们可以指定 11ty 只从一个目录内读取数据,这样可以不用把所有东西都铺在项目根目录下。比如,我的文章是放在 content/ 目录下的,那么就可以指定:

eleventyConfig.setInputDirectory('content/**')

注意到指定此项目后会连带更改 11ty 的特殊目录 _includes 的位置,它现在会在 content/_includes 下寻找模板文件,所以要保持之前的代码可用,需要将 hello.md 移动到 content/hello.md; 将 _includes/ 移动到 content/_includs. 再次编译之后,站点结构应保持不变。

输出目录不会随此项而更改。

指定复制目录

有些文件,比如图片、样式或者脚本库,是要复制到最终产物内的。11ty 提供了直通复制功能,可以用于自动复制这些文件到 _site 内。

比如,要复制 assets/ 文件夹到站点内,只需要写:

eleventyConfig.addPassthroughCopy('assets')

assets/ 文件夹在构建站点时将会被直接复制到 _site/assets/.

这个函数可以调用多次以添加更多要复制的文件夹,同时也支持使用通配符。11ty 在复制文件夹时会保持文件夹内的结构。

指定输出目录

11ty 默认将结果输出到 _site 文件夹,要更改这个文件夹名,如改为输出到 dist/,可以做如下配置:

eleventyConfig.setOutputDirectory("dist");

结论

11ty 是一个强大的静态站点生成器。当然,11ty 所给予的极高的自由度仍然需要我们自行折腾许多东西。在接下来的系列文章中,我们将探索从零开始构建一个博客的必要步骤。

正在加载评论……

发表评论

您的评论将由管理员审核后方可公开显示。

Your comments will be submitted to a human moderator and will only be shown publicly after approval.