用11ty搭建静态博客的技术选型与实战踩坑总结

Air-淑怡 框架 阅读 1,005
赞 12 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

11ty 真的很轻,但轻得让人容易忽略它“不帮你兜底”的本质。我用它搭过三个静态博客、两个文档站、一个公司内部知识库,每次上线前都要花半天修 path 和 permalink 的坑——不是功能不行,是它太直给,你写错一丢丢,生成的 HTML 就跑偏了。

用11ty搭建静态博客的技术选型与实战踩坑总结

最稳的一招:所有页面都显式声明 permalink,哪怕只是默认值。别信文档里写的“不设就自动推导”,我信了两次,结果首页生成在 /index.html,而 / 路径 404(Nginx 没配 rewrite),客户发截图问“为什么点链接打不开”,我一边改配置一边想删掉那行注释掉的 permalink

我现在统一这么写:

---
title: 关于我
layout: base.njk
permalink: /about/
---

注意结尾的斜杠。不加斜杠,11ty 会生成 about.html;加了,才生成 about/index.html,配合主流托管(Vercel、Cloudflare Pages、GitHub Pages)的默认行为,访问 /about/ 才能正常加载。这个细节,我踩了三次,最后一次直接把 permalink 写进 .eleventy.js 的全局数据里强制补斜杠。

这几种错误写法,别再踩坑了

第一种:在 Nunjucks 模板里用 {% raw %}{{ page.url }}{% endraw %} 做导航链接

看着方便,但它返回的是相对路径(比如 /posts/hello/),而你在 /posts/hello/ 页面里再用它,就变成 /posts/hello//posts/hello/ —— 你猜浏览器怎么处理双斜杠?它真就发请求了,然后 404。我调试了半天 Network 面板,才发现是这个鬼。

正确做法:统一用 url 过滤器(Eleventy 自带)或自己写个 absoluteUrl 工具函数:

// .eleventy.js
module.exports = function(eleventyConfig) {
  eleventyConfig.addFilter("absoluteUrl", function(url, siteUrl) {
    if (!url) return "";
    if (url.startsWith("http") || url.startsWith("//")) return url;
    return new URL(url, siteUrl).href;
  });
};

模板里这么用:

<a href="{{ '/about/' | absoluteUrl('https://jztheme.com') }}">关于我</a>

第二种:用 collections.all 排序文章,却不加 date 字段校验

我有篇草稿忘了写 date,结果它排在了所有文章最前面(undefined 在 sort 里比任何 Date 都小)。线上突然多出一篇“2020 年”的旧文顶在首页,客户以为我们偷偷上线了历史内容……后来我改成:

{%- set posts = collections.posts | sortObjectByDate('desc') | 
  filter((post) => post.date && !post.data.draft) -%}

sortObjectByDate 是 Eleventy 内置的安全排序,但还是得加 filter 去掉没 date 或 draft:true 的。

第三种:在 data 文件夹里写 JS 数据文件,却忘了 module.exports 返回对象

比如 _data/site.js 写成:

// ❌ 错误写法
const site = {
  title: "我的博客",
  url: "https://jztheme.com"
};
// 忘了这一行!
// module.exports = site;

结果 {{ site.title }} 渲染为空。11ty 不报错,也不警告,就默默跳过——你得手动 console.log 全局数据才能发现。现在我所有 _data/*.js 文件第一行必写 "use strict";,最后一行必写 module.exports = {...},靠 ESLint 插件强制检查。

实际项目中的坑

本地开发和生产环境的 base URL 不一致:开发时跑 localhost:8080,生产是 https://jztheme.com/docs/(子路径部署)。很多人直接在模板里写死 /css/main.css,结果 CSS 404。

我的解法是:只在 .eleventy.js 里配一次 pathPrefix,然后所有资源路径走 {{ "/css/main.css" | url }} 过滤器(Eleventy 内置):

// .eleventy.js
module.exports = {
  pathPrefix: "/docs/",
  // 其他配置...
};

模板中:

<link rel="stylesheet" href="{{ "/css/main.css" | url }}" />

这样开发时是 /css/main.css,生产自动变成 /docs/css/main.css。别手写拼接,也别用 site.url 拼——url 过滤器才是官方推荐的、且支持子路径的唯一可靠方式。

增量构建失效:我改了一个 .njk 模板,运行 npx @11ty/eleventy --serve 却没刷新。查了半天发现是用了 include 引入的局部模板没被监听——Eleventy 默认只监听当前文件和 _includes 下的文件,但如果你把局部模板放在 _includes/partials/,它就认不出来。

解决方案只有两个:要么把局部模板全放平级 _includes/ 下;要么在 .eleventy.js 里加 watch 监听:

module.exports = function(eleventyConfig) {
  eleventyConfig.addWatchTarget("_includes/partials/**/*");
};

我选后者,因为目录结构更清晰。不过提醒一句:watchTarget 加太多会拖慢热重载,别一股脑加 node_modules

结尾

以上是我用 11ty 搞定真实项目后总结的最佳实践,没有银弹,只有反复试错后的“暂时不崩”。有些方案不是最优的(比如用 absoluteUrl 过滤器而不是依赖 pathPrefix),但它简单、可预测、改起来不牵连其他地方。

如果你有更好的写法,比如怎么优雅地管理多语言 URL、怎么让 pagination 和 filter 组合得更干净,欢迎评论区交流——我已经准备好咖啡,随时看新思路。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论
Dev · 艳鑫
这篇文章让我感受到了技术的魅力,也让我更加热爱这个行业。
点赞 4
2026-02-17 12:25