11ty技术问询:如何将嵌套数据合并为单个集合项?
实现Eleventy多章节博客内容合并方案
针对你提出的「拆分博客章节文件、同目录存放资源」需求,以下是两种无需每个文章单独添加数据文件的实现方案:
方案一:全局eleventyComputed自动合并章节
通过全局数据文件自动读取当前文章的主文件与章节文件,合并为完整内容。
步骤1:创建内容读取工具
在项目根目录的_data文件夹下新建postContent.js:
const fs = require('fs'); const path = require('path'); const matter = require('gray-matter'); // 返回一个函数,接受文章文件路径,返回合并后的Markdown内容 module.exports = function() { return function(postInputPath) { const postDir = path.dirname(postInputPath); let contentParts = []; // 读取主文件index.md const mainFile = path.join(postDir, 'index.md'); if (fs.existsSync(mainFile)) { const { content } = matter(fs.readFileSync(mainFile, 'utf8')); contentParts.push(content); } // 读取sections目录下的章节文件,按数字前缀排序 const sectionsDir = path.join(postDir, 'sections'); if (fs.existsSync(sectionsDir)) { const sectionFiles = fs.readdirSync(sectionsDir) .filter(file => file.endsWith('.md')) .sort((a, b) => parseInt(a.split('.')[0]) - parseInt(b.split('.')[0])); sectionFiles.forEach(file => { const { content } = matter(fs.readFileSync(path.join(sectionsDir, file), 'utf8')); contentParts.push(content); }); } return contentParts.join('\n\n'); }; };
步骤2:全局配置eleventyComputed
在_data下新建eleventyComputed.js,自动为所有文章合并内容:
const getPostContent = require('./postContent.js')(); module.exports = { content: function(data) { // 仅处理posts目录下的index.md文件 if (data.page.inputPath.includes('/posts/') && data.page.fileSlug === 'index') { return getPostContent(data.page.inputPath); } // 非文章文件保留原内容 return data.content; } };
步骤3:配置资源复制
在.eleventy.js中添加资源目录的自动复制,确保图片能正确输出:
module.exports = function(eleventyConfig) { eleventyConfig.addPassthroughCopy('./posts/**/assets'); // 其他配置... };
方案二:自定义集合合并章节
通过创建自定义集合,直接生成包含合并内容的文章集合,适合需要统一管理文章数据的场景。
在.eleventy.js中添加集合配置:
const fs = require('fs'); const path = require('path'); const matter = require('gray-matter'); module.exports = function(eleventyConfig) { eleventyConfig.addCollection('mergedPosts', function(collectionApi) { // 获取所有posts目录下的index.md文件 const rawPosts = collectionApi.getFilteredByGlob('./posts/**/index.md'); return rawPosts.map(post => { const postDir = path.dirname(post.inputPath); let mergedContent = post.templateContent; // 读取并合并章节文件 const sectionsDir = path.join(postDir, 'sections'); if (fs.existsSync(sectionsDir)) { const sectionFiles = fs.readdirSync(sectionsDir) .filter(file => file.endsWith('.md')) .sort((a, b) => parseInt(a.split('.')[0]) - parseInt(b.split('.')[0])); sectionFiles.forEach(file => { const { content } = matter(fs.readFileSync(path.join(sectionsDir, file), 'utf8')); mergedContent += '\n\n' + content; }); } // 返回包含合并内容的文章对象 return { ...post.data, content: mergedContent, slug: post.data.slug || `/posts/${path.basename(postDir)}` }; }); }); // 复制资源目录 eleventyConfig.addPassthroughCopy('./posts/**/assets'); };
之后在模板中直接使用collections.mergedPosts即可获取合并后的文章数据:
{% for post in collections.mergedPosts %} <article> <h2><a href="{{ post.url }}">{{ post.title }}</a></h2> {{ post.content | safe }} </article> {% endfor %}
关键细节说明
- 图片路径:章节文件中直接使用相对路径引用图片即可,例如
,资源会随文章目录一起被复制到输出目录。 - 章节排序:通过文件名前缀数字(01.md、02.md)确保章节顺序正确,避免字符串排序导致的顺序混乱。
- 依赖安装:两种方案都需要安装
gray-matter解析Markdown的Front Matter:npm install gray-matter --save-dev
内容的提问来源于stack exchange,提问作者Denise
相关产品推荐
相关产品推荐

