Eleventy 中文教程

1. 什么是 Eleventy?

Eleventy(也称为 11ty)是一个简单、灵活的静态站点生成器 (SSG),使用 JavaScript 编写,由 Zach Leatherman 创建。它的设计理念是"零配置默认值,可配置的一切",旨在为开发者提供一个简单而强大的工具来构建静态网站。

1.1 Eleventy 的核心优势

  • 简单易用:零配置默认值,开箱即用,同时提供灵活的配置选项。
  • 多模板支持:支持多种模板语言,包括 Markdown、Nunjucks、Handlebars、Liquid、EJS 等。
  • 灵活的数据处理:支持多种数据源,包括前置元数据、数据文件、JavaScript 模块等。
  • 高性能:构建速度快,生成的静态文件体积小。
  • 可扩展性:通过插件系统扩展核心功能。
  • 无框架依赖:不强制使用特定的前端框架,可以与任何前端技术栈集成。
  • SEO 友好:生成的静态 HTML 对搜索引擎非常友好。
  • 部署便捷:生成的静态文件可以部署到任何静态托管服务。

2. 安装和初始化

2.1 使用 npm 初始化项目

# 创建项目目录
mkdir my-eleventy-site
cd my-eleventy-site

# 初始化 package.json
npm init -y

# 安装 Eleventy
npm install @11ty/eleventy

# 添加脚本
# 在 package.json 中添加:
# "scripts": {
#   "start": "eleventy --serve",
#   "build": "eleventy"
# }

2.2 使用 Eleventy 模板

Eleventy 提供了多种官方模板,可以通过 GitHub 模板创建项目:

# 使用基础模板
git clone https://github.com/11ty/eleventy-base-blog.git my-eleventy-blog
cd my-eleventy-blog
npm install

# 启动开发服务器
npm start

2.3 验证安装

# 检查 Eleventy 版本
npx eleventy --version

# 启动开发服务器
npm start

# 访问 http://localhost:8080

# 构建静态文件
npm run build

# 查看构建输出
ls _site

3. 基本目录结构

Eleventy 项目的默认目录结构如下:

my-eleventy-site/
├── src/             # 源代码目录(可选,需在配置中指定)
│   ├── _data/       # 数据文件目录
│   ├── _includes/    # 布局和组件目录
│   ├── posts/        # Markdown 文章
│   ├── pages/        # 其他页面
│   └── assets/       # 静态资源
├── _site/           # 构建输出目录(自动生成)
├── .eleventy.js     # Eleventy 配置文件
├── package.json     # 项目配置文件
└── README.md        # 项目说明文件

3.1 核心目录说明

  • **_data/**:存放数据文件,Eleventy 会自动加载这些数据供模板使用。
  • **_includes/**:存放布局、组件和其他可复用的模板片段。
  • **_site/**:构建输出目录,包含生成的静态 HTML、CSS 和 JavaScript 文件。
  • .eleventy.js:Eleventy 配置文件,用于自定义构建过程。

4. 核心功能

4.1 模板系统

Eleventy 支持多种模板语言,你可以根据需要选择最适合的一种:

4.1.1 Markdown

最常用的模板语言,适合编写内容:

---
title: "Hello Eleventy"
date: "2023-01-01"
description: "My first Eleventy post"
---

# {{ title }}

Published on {{ date }}

{{ description }}

This is my first Eleventy post!

4.1.2 Nunjucks

功能强大的模板语言,支持模板继承、包含和宏:

---
title: "About Us"
---

{% extends "_includes/layout.njk" %}

{% block content %}
  <h1>{{ title }}</h1>
  <p>Welcome to our website!</p>
{% endblock %}

4.1.3 Liquid

Shopify 使用的模板语言,语法简洁:

---
title: "Contact"
---

{% layout "_includes/layout.liquid" %}

<h1>{{ title }}</h1>
<p>Get in touch with us!</p>

4.2 数据处理

Eleventy 提供了灵活的数据处理能力,支持多种数据源:

4.2.1 前置元数据(Front Matter)

在模板文件顶部使用 YAML、JSON 或 TOML 格式添加元数据:

---
title: "My Post"
date: "2023-01-01"
author: "John Doe"
tags: ["eleventy", "static-site-generator"]
---

# {{ title }}

By {{ author }} on {{ date }}

4.2.2 数据文件

_data 目录中创建数据文件,Eleventy 会自动加载:

// _data/site.js
module.exports = {
  name: "My Eleventy Site",
  url: "https://example.com",
description: "A site built with Eleventy"
};

在模板中使用:

<h1>{{ site.name }}</h1>
<p>{{ site.description }}</p>

4.2.3 JavaScript 数据文件

使用 JavaScript 模块生成动态数据:

// _data/posts.js
const fs = require('fs');
const path = require('path');

module.exports = function() {
  const postsDirectory = path.join(__dirname, '../posts');
  const postFiles = fs.readdirSync(postsDirectory);
  
  return postFiles.map(filename => {
    const filePath = path.join(postsDirectory, filename);
    const content = fs.readFileSync(filePath, 'utf8');
    // 解析前置元数据和内容
    // ...
    return {
      filename,
      // 其他数据
    };
  });
};

4.3 布局系统

使用布局文件来保持网站的一致结构:

4.3.1 创建布局文件

<!-- _includes/layout.njk -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }} | {{ site.name }}</title>
  <link rel="stylesheet" href="/css/style.css">
</head>
<body>
  <header>
    <h1><a href="/">{{ site.name }}</a></h1>
  </header>
  <main>
    {% block content %}{% endblock %}
  </main>
  <footer>
    <p>&copy; {{ "now" | date("yyyy") }} {{ site.name }}</p>
  </footer>
</body>
</html>

4.3.2 在页面中使用布局

---
title: "Home"
layout: "_includes/layout.njk"
---

{% block content %}
  <h1>Welcome to {{ site.name }}</h1>
  <p>{{ site.description }}</p>
{% endblock %}

4.4 分页

Eleventy 提供了内置的分页功能,用于创建文章列表、产品目录等:

4.4.1 基本分页

---
title: "Blog"
layout: "_includes/layout.njk"
pagination:
  data: collections.posts
  size: 5
  alias: posts
---

{% block content %}
  <h1>{{ title }}</h1>
  
  {% for post in posts %}
    <article>
      <h2><a href="{{ post.url }}">{{ post.data.title }}</a></h2>
      <p>{{ post.data.description }}</p>
    </article>
  {% endfor %}
  
  <!-- 分页导航 -->
  {% if pagination.previousPageHref %}
    <a href="{{ pagination.previousPageHref }}">Previous</a>
  {% endif %}
  
  {% if pagination.nextPageHref %}
    <a href="{{ pagination.nextPageHref }}">Next</a>
  {% endif %}
{% endblock %}

4.5 短路器(Shortcodes)

短路器是 Eleventy 中的可重用模板片段,类似于组件:

4.5.1 创建短路器

// .eleventy.js
module.exports = function(eleventyConfig) {
  // 同步短路器
  eleventyConfig.addShortcode('user', function(name, avatar) {
    return `<div class="user"><img src="${avatar}" alt="${name}"><span>${name}</span></div>`;
  });
  
  // 异步短路器
  eleventyConfig.addAsyncShortcode('recentPosts', async function(count) {
    const posts = await getRecentPosts(count);
    return posts.map(post => `<li><a href="${post.url}">${post.title}</a></li>`).join('');
  });
};

4.5.2 在模板中使用短路器

{% block content %}
  <h1>About Us</h1>
  
  <h2>Our Team</h2>
  {% user "John Doe", "/images/john.jpg" %}
  {% user "Jane Smith", "/images/jane.jpg" %}
  
  <h2>Recent Posts</h2>
  <ul>
    {% recentPosts 5 %}
  </ul>
{% endblock %}

4.6 过滤器

过滤器用于转换模板中的数据:

4.6.1 创建过滤器

// .eleventy.js
module.exports = function(eleventyConfig) {
  // 添加日期格式化过滤器
  eleventyConfig.addFilter('formatDate', function(date, format) {
    return new Intl.DateTimeFormat('en-US', { 
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    }).format(new Date(date));
  });
  
  // 添加截断文本过滤器
  eleventyConfig.addFilter('truncate', function(text, length) {
    if (text.length <= length) return text;
    return text.substring(0, length) + '...';
  });
};

4.6.2 在模板中使用过滤器

{% block content %}
  <article>
    <h2>{{ title }}</h2>
    <p>Published on {{ date | formatDate }}</p>
    <p>{{ content | truncate(100) }}</p>
  </article>
{% endblock %}

3. 基本目录结构

Eleventy 项目的推荐目录结构如下:

my-eleventy-site/
├── src/             # 源代码目录
│   ├── _data/       # 数据文件目录
│   │   ├── site.js   # 站点配置数据
│   │   └── posts.js  # 文章数据
│   ├── _includes/    # 布局和组件目录
│   │   ├── layout.njk    # 主布局
│   │   ├── header.njk    # 头部组件
│   │   └── footer.njk    # 底部组件
│   ├── posts/        # Markdown 文章
│   │   ├── post-1.md
│   │   └── post-2.md
│   ├── pages/        # 其他页面
│   │   ├── index.njk     # 首页
│   │   ├── about.njk     # 关于页
│   │   └── contact.njk   # 联系页
│   └── assets/       # 静态资源
│       ├── css/      # CSS 文件
│       ├── js/       # JavaScript 文件
│       └── images/   # 图像文件
├── _site/           # 构建输出目录(自动生成)
├── .eleventy.js     # Eleventy 配置文件
├── package.json     # 项目配置文件
└── README.md        # 项目说明文件

4. 核心功能

4.1 模板系统

Eleventy 支持多种模板语言,你可以根据需要选择最适合的一种:

模板语言 文件扩展名 特点
Markdown .md 简洁易读,适合编写内容
Nunjucks .njk 功能强大,支持模板继承
Liquid .liquid 语法简洁,Shopify 使用
Handlebars .hbs 简单易用,适合简单模板
EJS .ejs JavaScript 嵌入式模板
Mustache .mustache 逻辑-less 模板
Haml .haml 简洁的缩进语法
Pug .pug 缩进语法,简洁优雅

4.2 数据处理

Eleventy 支持多种数据源,包括:

  1. 前置元数据(Front Matter):在文件顶部使用 YAML、JSON 或 TOML 格式
  2. 数据文件:在 _data 目录中的 JSON、YAML、JavaScript 文件
  3. 全局数据:在 .eleventy.js 中通过 addGlobalData 添加
  4. 计算数据:在模板中使用 JavaScript 计算

4.3 布局系统

Eleventy 的布局系统允许你创建可重用的页面结构:

  • 布局继承:使用 extends 指令继承基础布局
  • 块内容:使用 block 指令定义和覆盖布局中的内容区域
  • 包含文件:使用 include 指令包含可重用的模板片段

4.4 集合(Collections)

集合是 Eleventy 中的内容分组,默认情况下,所有 Markdown 文件会被添加到 collections.all 集合中。你可以通过前置元数据中的 tags 属性创建自定义集合:

---
title: "My First Post"
tags: ["blog", "tutorial"]
---

然后在模板中使用:

{% for post in collections.blog %}
  <article>
    <h2><a href="{{ post.url }}">{{ post.data.title }}</a></h2>
  </article>
{% endfor %}

4.5 分页

Eleventy 提供了内置的分页功能,用于创建文章列表、产品目录等:

pagination:
  data: collections.blog
  size: 10
  alias: posts
  reverse: true

4.6 短路器和过滤器

  • 短路器:可重用的模板片段,类似于组件
  • 过滤器:用于转换模板中的数据,如日期格式化、文本截断等

4.7 插件系统

Eleventy 通过插件系统扩展核心功能,常用的插件包括:

  • @11ty/eleventy-plugin-rss:生成 RSS 订阅源
  • @11ty/eleventy-plugin-syntaxhighlight:代码语法高亮
  • @11ty/eleventy-plugin-images-responsiver:响应式图像
  • @11ty/eleventy-navigation:网站导航

5. 高级特性

5.1 增量构建

Eleventy 支持增量构建,只重新构建修改过的文件,提高开发效率:

# 启用增量构建
npx eleventy --incremental

# 在开发服务器中启用
npx eleventy --serve --incremental

5.2 环境变量

使用环境变量区分开发和生产环境:

// .eleventy.js
module.exports = function(eleventyConfig) {
  const isProduction = process.env.NODE_ENV === 'production';
  
  if (isProduction) {
    // 生产环境配置
    eleventyConfig.addFilter('cdnUrl', function(url) {
      return `https://cdn.example.com${url}`;
    });
  } else {
    // 开发环境配置
    eleventyConfig.addFilter('cdnUrl', function(url) {
      return url;
    });
  }
};

5.3 内容安全策略

使用 Eleventy 生成内容安全策略(CSP)头:

// .eleventy.js
module.exports = function(eleventyConfig) {
  eleventyConfig.addShortcode('csp', function() {
    return `
      <meta http-equiv="Content-Security-Policy" content="
        default-src 'self';
        script-src 'self' https://cdn.example.com;
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https://cdn.example.com;
      ">
    `;
  });
};

5.4 国际化

使用数据文件和过滤器实现国际化:

// _data/i18n.js
module.exports = {
  en: {
    welcome: "Welcome",
    about: "About Us",
    contact: "Contact"
  },
  zh: {
    welcome: "欢迎",
    about: "关于我们",
    contact: "联系我们"
  }
};

// .eleventy.js
module.exports = function(eleventyConfig) {
  eleventyConfig.addFilter('t', function(key, lang = 'en') {
    return i18n[lang][key] || key;
  });
};

在模板中使用:

<h1>{{ 'welcome' | t(lang) }}</h1>
<a href="/about/">{{ 'about' | t(lang) }}</a>
<a href="/contact/">{{ 'contact' | t(lang) }}</a>

6. 最佳实践

6.1 性能优化

  • 使用增量构建:在开发过程中使用 --incremental 选项提高构建速度。
  • 优化图像:使用响应式图像,压缩图像文件大小。
  • 代码拆分:将 CSS 和 JavaScript 拆分为多个文件,减少初始加载体积。
  • 缓存策略:为静态资源设置适当的缓存头。
  • 预加载关键资源:使用 &lt;link rel=&quot;preload&quot;&gt; 预加载关键资源。

6.2 目录结构

my-eleventy-site/
├── src/             # 源代码目录
│   ├── _data/       # 数据文件
│   ├── _includes/    # 布局和组件
│   ├── posts/        # Markdown 文章
│   ├── pages/        # 其他页面
│   └── assets/       # 静态资源
├── _site/           # 构建输出目录
├── .eleventy.js     # Eleventy 配置
├── package.json     # 项目配置
└── README.md        # 项目说明

6.3 部署策略

Eleventy 生成的静态文件可以部署到任何静态托管服务:

6.3.1 Netlify

  1. 将代码推送到 GitHub、GitLab 或 Bitbucket
  2. 在 Netlify 上导入项目
  3. 配置构建命令:npm run build
  4. 配置发布目录:_site
  5. 部署完成后获得一个 URL

6.3.2 Vercel

  1. 将代码推送到 GitHub、GitLab 或 Bitbucket
  2. 在 Vercel 上导入项目
  3. 配置构建命令:npm run build
  4. 配置发布目录:_site
  5. 部署完成后获得一个 URL

6.3.3 GitHub Pages

  1. 安装 gh-pages 插件:npm install gh-pages
  2. package.json 中添加脚本:&quot;deploy&quot;: &quot;eleventy &amp;&amp; gh-pages -d _site&quot;
  3. 运行部署命令:npm run deploy
  4. 部署到 https://&lt;username&gt;.github.io/&lt;repository-name&gt;

6.4 安全最佳实践

  • 环境变量:使用 .env 文件存储敏感信息,不要将其提交到版本控制系统。
  • 输入验证:验证用户输入,防止 XSS 和注入攻击。
  • 依赖管理:定期更新依赖,修复安全漏洞。
  • HTTPS:确保网站使用 HTTPS 协议。
  • 内容安全策略:配置适当的 CSP 头,防止恶意脚本执行。

7. 常见问题与解决方案

7.1 模板渲染问题

问题:模板中的变量不显示

解决方案

  • 检查变量名称是否正确
  • 确保数据文件存在且格式正确
  • 检查前置元数据是否正确设置
  • 使用 console.log() 在数据文件中调试数据

7.2 构建失败

问题eleventy 命令执行失败

解决方案

  • 检查控制台错误信息,定位具体问题
  • 确保所有依赖都已正确安装
  • 检查 .eleventy.js 配置文件是否有语法错误
  • 检查模板文件是否有语法错误

7.3 路径问题

问题:静态资源路径不正确

解决方案

  • 使用根相对路径(以 / 开头)引用静态资源
  • 检查 pathPrefix 配置是否正确
  • 在开发服务器中使用正确的端口访问网站

7.4 分页问题

问题:分页导航不显示或链接不正确

解决方案

  • 检查分页配置是否正确
  • 确保 data 配置指向有效的集合
  • 检查 size 配置是否合理
  • 使用 pagination.previousPageHrefpagination.nextPageHref 生成导航链接

7.5 插件问题

问题:插件不工作或导致构建失败

解决方案

  • 确保插件已正确安装
  • 检查插件配置是否正确
  • 查看插件的官方文档,了解正确的使用方法
  • 尝试更新插件到最新版本

8. 总结

Eleventy 是一个简单而强大的静态站点生成器,它的设计理念是"零配置默认值,可配置的一切",为开发者提供了一个灵活、高效的工具来构建静态网站。

通过本教程,你应该已经了解了 Eleventy 的基本概念、核心功能和最佳实践。Eleventy 非常适合构建博客、企业网站、文档网站等各种类型的静态网站,它的简单性和灵活性使得前端开发变得更加愉悦和高效。

Eleventy 的优势在于:

  1. 简单易用:零配置默认值,开箱即用
  2. 多模板支持:支持多种模板语言,满足不同开发者的需求
  3. 灵活的数据处理:支持多种数据源,简化数据管理
  4. 高性能:构建速度快,生成的静态文件体积小
  5. 可扩展性:通过插件系统扩展核心功能
  6. 无框架依赖:可以与任何前端技术栈集成

无论你是构建个人博客还是企业网站,Eleventy 都能为你提供出色的解决方案。随着 Eleventy 的不断发展,新的特性和改进不断推出,建议你关注官方文档和更新日志,保持对最新功能的了解。

« 上一篇 Gatsby 中文教程 下一篇 » Vite 中文教程