静态网站生成利器 Eleventy

什么是 Eleventy?

Eleventy (或 11ty )是一个 静态网站生成器(Static Site Generator),用 JavaScript 编写,具有以下特点:

  • 简单易用,零配置即可运行
  • 支持多种模板语言(如 HTML、Markdown、Nunjucks、Handlebars、Pug 等)
  • 无数据库、无 CMS,适合构建博客、文档站、作品集等
  • 构建速度快,输出纯静态文件,便于部署

官网:www.11ty.dev(中文文档也已提供)

生成网站

从最基本的功能来看,Eleventy 和其他静态网站生成器类似:它会读取一个包含源文件的文件夹,处理这些输入内容,然后输出一个文件夹,其中的内容就是最终的网站。

假设你有一个包含 HTML 和 Markdown 文件的目录,结构如下所示。只要完成配置并运行 11ty(我推荐使用命令 npx @11ty/eleventy --serve,它会启动一个支持热重载的本地服务器------建议你将这条命令添加到 package.json 的 scripts 中),Eleventy 就会将这些文件逐一转换成最终的网站,默认输出到名为 _site 的文件夹中。

注意,非 index 的页面文件会被自动转换为文件夹,并在其中生成一个 index.html。这样做可以让 URL 更加简洁美观:例如,访问 /team 而不是 /team.html,网址看起来更干净、更专业。

这基本上就是大多数静态网站生成器的工作方式。但 11ty 的特别之处在于,它被设计得极具可定制性。你可以自由设置它从哪里读取输入文件、处理哪些类型的文件、忽略哪些内容,以及最终网站的输出目录。你还可以混合使用多种模板引擎,添加"过滤器"来处理模板中的内容------比如将纯文本中的链接自动转换为可点击的超链接。此外,你可以通过 JSON 文件或 JavaScript 函数为页面提供数据(甚至可以在构建时从外部 API 获取数据),从而动态生成页面内容。功能远不止这些,而我只用了几个小时,就已经意识到还有大量功能是我尚未发现的。

官方文档一开始让我有些困惑,因为它对"如何搭建你的网站"几乎不持任何立场,也没有引导你思考如何根据自身需求进行定制,尽管它本身确实内置了一些默认行为。接下来,我会按照时间顺序,一步步带你了解我是如何发现这些默认行为,并根据自己的需要进行调整的。


使用 .eleventyignore 忽略文件

我遇到的第一个问题,是 GitHub 仓库中的 README.md 被自动处理并生成到了最终网站中,变成了 makespace.fun/readme 这样的页面。其实,Eleventy 支持像 .gitignore 那样忽略特定文件和文件夹。你只需创建一个名为 .eleventyignore 的文件,然后把想要忽略的文件或目录名逐行写入其中即可。例如:

复制代码
README.md
_drafts/
secret.txt

这样,这些文件就不会被包含在生成的网站中了。这个机制简单却非常实用,能帮助你保持输出内容的整洁。

配置 Eleventy:.eleventy.js

使用"直通文件复制"保留原始资源

接着,我发现了一个问题:Eleventy 并没有把我的样式文件和静态资源复制到最终生成的网站中。默认情况下,它只处理 .html.md 文件,像 /css/assets 这样的重要文件夹并不会被自动包含进来。这是我遇到的第一个障碍!

经过一番摸索,我发现了 .eleventy.js 配置文件的存在。这个文件很容易创建,也是你进行大部分自定义配置的核心所在。

js 复制代码
// 项目根目录下的 .eleventy.js
module.exports = function(eleventyConfig) {
  // 自定义配置写在这里
}

为了让 /css/assets 文件夹顺利进入最终输出目录,我需要添加如下代码,启用"直通复制"(Passthrough Copy)功能:

js 复制代码
module.exports = function(eleventyConfig) {
  // 这些文件夹将原封不动地复制到输出目录
  eleventyConfig.addPassthroughCopy("assets");
  eleventyConfig.addPassthroughCopy("css");
}

你甚至可以更灵活一些,在复制过程中重命名或更改目标路径

除了资源文件的复制,你还可以在配置文件中自定义模板引擎、模板存放位置、添加自定义过滤器等。下面我会结合具体概念进一步介绍这些功能。


模板系统(Templating)

到目前为止,Eleventy 给我的感受还不算特别惊艳。虽然看到资源文件能顺利复制过去让我挺高兴,也觉得"挑选部分文件生成简洁 URL"这个想法不错------但整体流程还是有些繁琐。好在接下来我接触到了模板功能,这才真正体会到它的价值。

当时我有两个页面:首页和团队页。它们都包含一些重复的结构,比如 <head> 中的 <meta> 标签、导航栏和页脚。为了减少重复代码,我决定使用模板来统一管理这些共用部分。

自定义模板引擎

下面的例子中,我会继续使用 Eleventy 默认的 LiquidJS 模板引擎,因为我之前用过 Jekyll。但你完全可以在 .eleventy.js 配置文件中更换默认引擎,比如改用 Nunjucks 或 Pug。

js 复制代码
module.exports = function(eleventyConfig) {
  return {
    markdownTemplateEngine: "njk",  // 将 Markdown 文件使用 Nunjucks 引擎处理
    htmlTemplateEngine: "pug"       // 将 HTML 文件使用 Pug 引擎处理
  }
}

注意:当你使用 return 语法时,可以覆盖默认的模板引擎设置。


博客文章示例:布局与组件复用

由于我之前用过 Jekyll,对"布局模板"和"包含组件"的概念并不陌生。假设你有一篇用 Markdown 写的博客文章,你想让它套用一个统一的页面模板,而这个模板又需要包含页脚这类可复用的组件。

hello-09.02.md(文章内容)

md 复制代码
---
layout: blogpost.liquid
title: 你好!这是我的第一篇博客
---
# 哇,这是一篇超酷的博客!
是的,这是用 Markdown 写的!太棒了......

文件顶部 --- 之间的部分叫做 Front Matter(前置数据),它是一种为文件附加元数据的方式,供模板引擎读取。在这个例子中,我告诉 Eleventy:这篇博客要使用 blogpost.liquid 作为布局模板,并且把 title 变量传给模板使用。

blogpost.liquid(布局模板)

html 复制代码
---
title: 默认标题
---
<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    {{ content }}
    {% include footer %}
  </body>
</html>

注意,这个布局文件本身也有 Front Matter!它提供了一个默认标题,以防某个页面没有在 Front Matter 中指定标题。

  • {{ }} 双花括号用于插入变量内容
  • {% %} 花括号加百分号则用于执行模板逻辑,比如 include(包含)、if 条件判断,甚至 for 循环。

这样一来,你就可以轻松实现页面结构的复用和动态内容的注入,大大提升了开发效率和维护性。

高级模板功能:布局链与包含变量

Eleventy 的模板系统不仅支持基础的复用,还允许你进行更复杂的组合操作,比如布局嵌套(链式布局)对组件传递参数

布局链(Layout Chaining)

你可以让一个布局模板本身也使用另一个布局作为其"父级"。例如,blogpost.liquid 使用 base.liquid 作为外层结构,而 base.liquid 负责定义 HTML 的基本骨架。这样就能实现多层结构的复用,避免重复代码。

向包含组件传递变量

更强大的是,你可以在 include 时传入变量,从而动态控制组件的行为。比如,为当前激活的导航项添加高亮样式:

html 复制代码
<!-- index.html -->
{% include nav.liquid, current: 'index' %}

<!-- team.html -->
{% include nav.liquid, current: 'team' %}
html 复制代码
<!-- _includes/nav.liquid -->
<ul>
  <li class="{% if current == 'index' %}current{% endif %}">
    <a href="/">首页</a>
  </li>
  <li class="{% if current == 'team' %}current{% endif %}">
    <a href="/team">团队</a>
  </li>
</ul>

通过传入 current 参数,导航组件能自动判断哪个页面是当前页,并为其添加 current 类,实现视觉上的选中状态。这种模式非常适合构建可复用、智能化的 UI 组件。


利用 Front Matter 控制资源加载

你还可以在页面的 Front Matter 中指定特定资源,比如加载不同的 CSS 文件:

yaml 复制代码
---
css: team.css
---

然后在布局模板中引用:

html 复制代码
<link rel="stylesheet" href="/css/{{ css }}">

这样一来,每个页面都可以灵活地加载自己所需的样式表,而无需在每个 HTML 文件中手动写死路径。


自定义包含文件目录

默认情况下,Eleventy 会在项目中查找名为 _includes 的文件夹来存放可复用的模板片段。但你完全可以自定义这个目录名。在 .eleventy.js 配置文件中使用 dir.includes 选项即可:

js 复制代码
module.exports = function(eleventyConfig) {
  return {
    dir: {
      includes: "components"  // 现在会从 /components 文件夹中查找 include 文件
    }
  };
};

这让你可以根据项目结构自由组织模板组件,提升可读性和维护性。


数据管理(Data)

从静态 HTML 到结构化数据

当我逐步将页面模板化后,我发现很多信息是直接写死在 HTML 中的------比如团队页面上列出的几十位合作者姓名。每次增删成员都要手动修改 HTML,非常容易出错且难以维护。这时我意识到:这些内容完全可以提取成结构化的数据文件,比如 JSON,甚至未来还能从 API 动态获取!

Eleventy 的数据体系

Eleventy 提供了强大而灵活的数据管理机制,支持多种方式定义数据:

  • Front Matter:在单个文件中定义页面专属数据。
  • 文件夹级数据文件 (如 team.jsonteam.11tydata.js):为某一组页面提供共享数据。
  • 全局数据文件 :放在 _data 文件夹中,所有模板都可以访问。

数据级联(Data Cascading)

Eleventy 的核心概念之一是 数据级联(Data Cascading)。它定义了数据的查找顺序和合并规则。简单来说:

数据优先级:页面 Front Matter > 文件夹数据 > 全局数据

如果有同名变量,优先级高的会覆盖低的。同时,对象类型的值会自动深度合并,而不是简单替换。例如:

  • 全局 _data/site.json 定义了网站标题和默认描述。
  • 某个页面的 Front Matter 可以只覆盖标题,描述则沿用全局值。

这种机制让你既能统一管理通用信息,又能灵活定制个别页面。

使用目录数据文件存储页面专属数据

以上面提到的"团队页"为例,我决定将团队成员的信息从 HTML 中抽离出来,单独存放到一个 JSON 文件中。为此,我把原来的 team.html 重命名为 team/index.html(这样可以让 HTML 与数据文件共存于同一目录),然后在 team/ 文件夹中创建了一个名为 team.11tydata.json 的文件。

只需这么做,Eleventy 就会自动识别这个文件,并将其内容作为该目录下所有模板的可用数据------无需任何额外配置

json 复制代码
{
  "coconspirators": ["张三", "李四", "王五", "..."]
}

接着,在模板中就可以直接使用这些数据了:

html 复制代码
<ul>
  {% for person in coconspirators %}
    <li>{{ person }}</li>
  {% endfor %}
</ul>

这样一来,每次增减成员时,只需修改 JSON 文件,无需触碰 HTML 结构,维护起来既安全又高效。

小结数据管理

通过高级模板功能结构化数据管理,Eleventy 不再只是一个静态文件处理器,而是一个真正强大的内容生成引擎。你可以:

  • 布局链 构建复杂页面结构
  • 带参数的 include 实现智能组件
  • 自定义 include 目录 组织项目结构
  • JSON 或 JS 数据文件 替代硬编码内容
  • 利用 数据级联 实现灵活而一致的配置管理

这些特性共同构成了一个高效、可维护的现代静态网站开发体验。


过滤器(Filters):让数据处理更智能

当我把静态 HTML 内容逐步迁移到 JSON 数据时,遇到了一个问题:有些内容包含 HTML 标签,比如团队成员简介中的链接,而且这些链接需要在新标签页中打开(target="_blank")。

我当然可以把原始 HTML 直接写进 JSON:

json 复制代码
{
  "team": [
    {
      "bio": "此人曾在 <a href=\"https://makespacefoundation.org\" target=\"_blank\">MakeSpace</a> 工作。"
    }
  ]
}

但这不仅让 JSON 显得臃肿混乱,还容易出错,尤其是每个链接都要手动加 target="_blank",非常繁琐。 更好的做法是:用 Markdown 写内容,再通过过滤器自动处理链接行为

这时,Eleventy 的 过滤器(Filter) 功能就派上用场了!过滤器允许你在模板中对数据进行加工,语法简洁明了:

html 复制代码
{{ bio | markdownify | externalLinks }}

它会先将 Markdown 转为 HTML,再为所有外部链接自动添加 target="_blank"rel="noopener" 等安全属性。


创建自定义过滤器

.eleventy.js 配置文件中添加过滤器非常简单:

js 复制代码
module.exports = function(eleventyConfig) {
  // 配置 markdown-it 解析器
  const options = {
    html: true,
    breaks: true,
    linkify: true,
  };
  const md = require("markdown-it")(options);

  // 添加 markdownify 过滤器
  eleventyConfig.addFilter("markdownify", function(value) {
    return md.render(value || "");
  });

  // 可以继续添加其他过滤器......
};

现在你就可以在任何模板中使用 {{ content | markdownify }},将 Markdown 文本转换为 HTML。

如果还想让所有链接在新窗口打开,可以进一步扩展 markdown-it 的渲染规则,或使用现成的插件(如 markdown-it-for-inline),为每个链接自动注入 target="_blank" 属性。配置方式与上面类似,只需在 md 实例初始化时添加相应插件即可。


更多值得探索的功能

希望这篇指南能帮你理解 Eleventy 的核心魅力所在------它不只是一个静态站点生成器,更是一个灵活、可编程的内容构建系统。

接下来,我计划继续完善网站:

  • 添加更多静态页面,并逐步建立统一的设计系统。
  • 搭建博客功能 ,尝试使用 Eleventy 的:
  • 将本地 JSON 数据迁移到 Google Sheets:让非技术人员也能轻松编辑内容,无需访问代码仓库。
  • 编写自定义短代码(Shortcodes):创建可复用的富媒体组件,比如嵌入视频、图表或引用框。

Eleventy 的强大之处在于,它既足够简单,让你快速上手;又足够开放,支持深度定制。随着你对它的了解加深,你会发现越来越多提升效率的方式。

真正的自由,来自于对工具的掌控。而 Eleventy,正赋予你这种自由。

参考

相关推荐
iMonster1 分钟前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢9 分钟前
antd渐变色边框按钮
前端
元直数字电路验证29 分钟前
Jakarta EE Web 聊天室技术梳理
前端
wadesir32 分钟前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛32 分钟前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠34 分钟前
前端面试八股复习心得
开发语言·前端·javascript
9***Y4835 分钟前
前端动画性能优化
前端
网络点点滴37 分钟前
Vue3嵌套路由
前端·javascript·vue.js
牧码岛1 小时前
Web前端之Vue+Element打印时输入值没有及时更新dom的问题
前端·javascript·html·web·web前端
小二李1 小时前
第8章 Node框架实战篇 - 文件上传与管理
前端·javascript·数据库