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 start2.3 验证安装
# 检查 Eleventy 版本
npx eleventy --version
# 启动开发服务器
npm start
# 访问 http://localhost:8080
# 构建静态文件
npm run build
# 查看构建输出
ls _site3. 基本目录结构
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>© {{ "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 支持多种数据源,包括:
- 前置元数据(Front Matter):在文件顶部使用 YAML、JSON 或 TOML 格式
- 数据文件:在
_data目录中的 JSON、YAML、JavaScript 文件 - 全局数据:在
.eleventy.js中通过addGlobalData添加 - 计算数据:在模板中使用 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: true4.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 --incremental5.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 拆分为多个文件,减少初始加载体积。
- 缓存策略:为静态资源设置适当的缓存头。
- 预加载关键资源:使用
<link rel="preload">预加载关键资源。
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
- 将代码推送到 GitHub、GitLab 或 Bitbucket
- 在 Netlify 上导入项目
- 配置构建命令:
npm run build - 配置发布目录:
_site - 部署完成后获得一个 URL
6.3.2 Vercel
- 将代码推送到 GitHub、GitLab 或 Bitbucket
- 在 Vercel 上导入项目
- 配置构建命令:
npm run build - 配置发布目录:
_site - 部署完成后获得一个 URL
6.3.3 GitHub Pages
- 安装
gh-pages插件:npm install gh-pages - 在
package.json中添加脚本:"deploy": "eleventy && gh-pages -d _site" - 运行部署命令:
npm run deploy - 部署到
https://<username>.github.io/<repository-name>
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.previousPageHref和pagination.nextPageHref生成导航链接
7.5 插件问题
问题:插件不工作或导致构建失败
解决方案:
- 确保插件已正确安装
- 检查插件配置是否正确
- 查看插件的官方文档,了解正确的使用方法
- 尝试更新插件到最新版本
8. 总结
Eleventy 是一个简单而强大的静态站点生成器,它的设计理念是"零配置默认值,可配置的一切",为开发者提供了一个灵活、高效的工具来构建静态网站。
通过本教程,你应该已经了解了 Eleventy 的基本概念、核心功能和最佳实践。Eleventy 非常适合构建博客、企业网站、文档网站等各种类型的静态网站,它的简单性和灵活性使得前端开发变得更加愉悦和高效。
Eleventy 的优势在于:
- 简单易用:零配置默认值,开箱即用
- 多模板支持:支持多种模板语言,满足不同开发者的需求
- 灵活的数据处理:支持多种数据源,简化数据管理
- 高性能:构建速度快,生成的静态文件体积小
- 可扩展性:通过插件系统扩展核心功能
- 无框架依赖:可以与任何前端技术栈集成
无论你是构建个人博客还是企业网站,Eleventy 都能为你提供出色的解决方案。随着 Eleventy 的不断发展,新的特性和改进不断推出,建议你关注官方文档和更新日志,保持对最新功能的了解。