再一次体验 Web 开发狂野的那一面。
11ty (Eleventy) 是一个多模板静态网站生成器。多模板意味着它支持不止一个模板语言——实际上它支持 11 个模板语言,所以叫 11ty. 不过在整个工程中,我们只会用到 liquid
, njk
, js
和 markdown
四项;其中以 liquid
和 markdown
两项为主。
项目结构
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 格式指定元数据。最明显的更改是对于布尔类型数据,只能使用 true
和 false
; yes
和 no
会被看作字符串。
数据层叠
当通过 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 所给予的极高的自由度仍然需要我们自行折腾许多东西。在接下来的系列文章中,我们将探索从零开始构建一个博客的必要步骤。
正在加载评论……