如何在 Svelte 中正确实现去重的“相关文章”功能

2次阅读

如何在 Svelte 中正确实现去重的“相关文章”功能

本文详解如何修复 Svelte 中因嵌套 {#each} 导致的相关文章重复渲染问题,通过预过滤数据替代模板内多层条件判断,确保每篇相关文章仅显示一次。

本文详解如何修复 svelte 中因嵌套 `{#each}` 导致的相关文章重复渲染问题,通过预过滤数据替代模板内多层条件判断,确保每篇相关文章仅显示一次。

在构建博客类 Svelte 应用时,“相关文章”组件常需根据当前文章的标签(tags)匹配其他已发布文章。但若直接在模板中使用嵌套循环(如外层遍历所有文章、内层遍历其标签),极易引发重复渲染——例如,当前文章含 3 个标签,而另一篇候选文章恰好匹配其中任意一个,该候选文章就会被渲染 3 次(每个匹配标签触发一次

  • )。

    原始代码的问题根源在于逻辑耦合在模板层:

    {#each posts as post}   {#each post.meta.tags as tag}     {#if currentPostTags.includes(tag)}       <!-- 匹配即渲染 → 同一篇文章可能被多次插入 -->       <li>...</li>     {/if}   {/each} {/each}

    这种写法违背了“数据驱动视图”的原则:模板应负责呈现,而非筛选与去重

    ✅ 正确解法是将匹配与去重逻辑前置到 JavaScript 层,在组件加载后一次性生成干净的 relatedPosts 数组:

    <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 &&         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> {/if}

    ? 关键改进说明:

    • 逻辑分离:filterRelatedPosts() 将业务规则(排除自身、检查发布时间、计算标签交集)集中处理,模板仅做扁平化渲染;
    • 天然去重:filter() 对每篇文章只执行一次判断,匹配即保留一项,彻底避免重复
    • 性能更优:避免模板中重复计算 currentPostTags.includes(tag),尤其当 tags 数组较大时;
    • 可维护性强:后续如需增加权重排序(如按共同标签数降序)、限制数量(.slice(0, 3))或添加缓存,均只需修改 js 层逻辑。

    ⚠️ 注意事项:

    • 若 currentPostTags 是响应式变量(如由 $: 声明),需改用 $: 响应式声明或 subscribe 监听变化,否则 onMount 中的快照无法自动更新;
    • 标签匹配默认区分大小写,如需忽略大小写,可统一转为小写再比较:currentPostTags.map(t => t.toLowerCase()).includes(tag.toLowerCase());
    • 生产环境建议对 getMarkdownPosts() 添加错误处理,避免白屏。

    通过将数据筛选从模板移至脚本层,你不仅解决了重复渲染问题,更让组件结构更符合 Svelte 的响应式设计哲学——让逻辑归逻辑,让视图归视图

  • text=ZqhQzanResources