
本文介绍如何在 Svelte 中正确筛选并去重展示“相关文章”,解决因嵌套 #each 导致同一文章被多次渲染的问题,核心是将标签匹配逻辑前置到 JavaScript 层完成过滤,而非在模板中双重遍历。
本文介绍如何在 svelte 中正确筛选并去重展示“相关文章”,解决因嵌套 `#each` 导致同一文章被多次渲染的问题,核心是将标签匹配逻辑前置到 javascript 层完成过滤,而非在模板中双重遍历。
在构建博客类 Svelte 应用时,常需为当前文章动态展示“相关文章”——即标题不同、已发布、且至少共享一个标签的文章。若直接在模板中使用嵌套 {#each}(如外层遍历所有文章、内层遍历其标签),会导致同一候选文章因匹配多个共同标签而被重复渲染多次。例如:当前文章含 ["svelte", "tutorial"],而某篇候选文章也含这两个标签,则该文章会在
- 中出现两次,严重破坏 UI 逻辑与用户体验。
根本原因在于:原始写法将筛选条件(是否相关)错误地放在了模板渲染阶段,且未做去重控制。正确的解法是——将筛选与去重完全交由 JS 逻辑处理,模板仅负责干净、线性的渲染。
以下是优化后的推荐实现:
<script>
import { getMarkdownPosts } from '$lib/utils/getPosts';
import { onMount } from 'svelte';
let relatedPosts = [];
export let currentPostTitle, currentPostTags;
onMount(async () => {
const allPosts = await getMarkdownPosts();
// ✅ 单次过滤:排除自身 + 未发布 + 无标签交集
relatedPosts = allPosts.filter(post => {
const { title, tags, published } = post.meta;
return (
title !== currentPostTitle &&
published === true &&
currentPostTags.some(tag => tags.includes(tag))
);
});
});
</script>
{#if relatedPosts.length > 0}
<h3>Related posts</h3>
<ul>
{#each relatedPosts as { slug, meta: { title } }}
<li><a href="/blog/{slug}"><h4>{title}</h4></a></li>
{/each}
</ul>
{:else}
<p>No related posts found.</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/ai/1763" title="Khroma"><img
src="https://img.php.cn/upload/ai_manual/000/969/633/68b6ceddca9b1981.png" alt="Khroma"></a>
<div class="aritcle_card_info flexColumn">
<a href="/ai/1763" title="Khroma">Khroma</a>
<p>AI调色盘生成工具</p>
</div>
<a href="/ai/1763" title="Khroma" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
{/if}✅ 关键改进点说明:
- 逻辑前置:filterRelatedPosts() 被整合进 onMount,确保异步数据加载完成后立即执行一次精准过滤,返回唯一、无重复的 relatedPosts 数组;
- 语义清晰的筛选条件:使用 currentPostTags.some(tag => tags.includes(tag)) 判断「至少一个标签匹配」,避免内层循环;
- 模板极简渲染:{#each relatedPosts} 仅作单层遍历,天然杜绝重复;
- 健壮性增强:显式检查 published === true(而非仅 if (published)),避免 falsy 值误判;增加空状态提示,提升用户体验。
⚠️ 注意事项:
- 若 currentPostTags 或 post.meta.tags 可能为 null/undefined,应在 filter 中添加防御性检查(如 Array.isArray(currentPostTags) && Array.isArray(tags));
- 标签匹配默认区分大小写。如需忽略大小写,可改用 currentPostTags.some(tag => tags.map(t => t.toLowerCase()).includes(tag.toLowerCase()));
- 对于大型文章库(>100 篇),可进一步加入按匹配标签数或发布时间排序(如 .sort((a, b) => b.meta.tags.filter(t => currentPostTags.includes(t)).length - a.meta.tags.filter(...).length)),提升相关性。
此方案兼顾性能、可读性与可维护性,符合 Svelte “逻辑在脚本中,视图只负责呈现”的设计哲学,是生产环境推荐的标准实践。









