You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte组件中目录重复问题排查及解决方案

问题:切换页面后右侧悬浮目录未更新为当前文章内容

当前Svelte组件实现了滚动时在右侧窗口展示文章目录的功能,但切换到另一篇带独立目录的文章时,右侧窗口仍显示上一篇的目录。目录由remark-toc自动生成无法修改。

原因分析

  • 组件仅在onMount时获取一次目录内容,单页应用路由切换后组件不会重新挂载,导致tableOfContents变量保留旧值
  • Intersection Observer未在页面切换时销毁并重新监听新目录元素,可能造成showAside状态异常

解决方案

1. 适配SvelteKit的修复代码

<script>
// @ts-nocheck
import { onMount, onDestroy } from 'svelte'
import { scale, fade } from 'svelte/transition'
import { page } from '$app/stores'

let tableOfContents = null
let showToc = false
let showAside = false
let observer = null

function getTableOfContents() {
    const postTableOfContentsEl = document.querySelector('#в-этой-статье + ol')
    if (postTableOfContentsEl) {
        tableOfContents = postTableOfContentsEl.outerHTML
    } else {
        tableOfContents = null
    }
}

function setupObserver() {
    // 清理旧的Observer
    if (observer) {
        const oldTarget = document.querySelector('#в-этой-статье + ol')
        oldTarget && observer.unobserve(oldTarget)
        observer.disconnect()
    }

    const targetEl = document.querySelector('#в-этой-статье + ol')
    if (!targetEl) {
        showAside = false
        return
    }
    
    observer = new IntersectionObserver(([entry]) => {
        showAside = entry.boundingClientRect.bottom < 0
    })
    observer.observe(targetEl)
}

onMount(() => {
    setupObserver()
    getTableOfContents()

    // 监听路由变化
    const unsubscribe = page.subscribe(() => {
        // 等待DOM渲染完成后更新
        setTimeout(() => {
            getTableOfContents()
            setupObserver()
            showToc = false
        }, 100)
    })

    return unsubscribe
})

onDestroy(() => {
    // 组件销毁时清理资源
    if (observer) {
        const targetEl = document.querySelector('#в-этой-статье + ol')
        targetEl && observer.unobserve(targetEl)
        observer.disconnect()
    }
})

function toggleToc() {
    showToc = !showToc
}
</script>

{#if tableOfContents}
    {#if showAside}
        <div in:fade={{ duration: 200 }} out:fade={{ duration: 200}}>
            {#if showToc}
                <div class="toc" class:active={showToc} transition:scale={{ start: 0, duration: 300, delay: 0 }}>
                    <p class="toc-title">В этой статье</p>
                    {@html tableOfContents}
                </div>
            {/if}
        </div>
        <button on:click={toggleToc} in:fade={{ duration: 200 }} out:fade={{ duration: 200}}>
            <svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" class="cf-icon-svg" viewBox="-2.5 0 19 19"><path d="M2.85 4.836a1.03 1.03 0 0 1-1.029 1.03h-.003a1.03 1.03 0 1 1 0-2.059h.003a1.03 1.03 0 0 1 1.03 1.03zm0 4.346a1.03 1.03 0 0 1-1.029 1.03h-.003a1.03 1.03 0 1 1 0-2.059h.003a1.03 1.03 0 0 1 1.03 1.03zm0 4.346a1.03 1.03 0 0 1-1.029 1.03h-.003a1.03 1.03 0 1 1 0-2.059h.003a1.03 1.03 0 0 1 1.03 1.03zm1.13-8.692a1.03 1.03 0 0 1 1.029-1.029h7.173a1.03 1.03 0 1 1 0 2.058H5.009a1.03 1.03 0 0 1-1.03-1.029zm9.231 4.347a1.03 1.03 0 0 1-1.029 1.029H5.009a1.03 1.03 0 1 1 0-2.059h7.173a1.03 1.03 0 0 1 1.03 1.03zm0 4.345a1.03 1.03 0 0 1-1.029 1.03H5.009a1.03 1.03 0 1 1 0-2.059h7.173a1.03 1.03 0 0 1 1.03 1.03z"/></svg>
        </button>
    {/if}
{/if}

2. 关键修改说明

  • 监听路由变化事件,触发目录重新获取和Observer重建
  • 每次路由切换后清理旧Observer,避免监听失效元素
  • 切换页面后默认关闭目录弹窗,优化用户体验
  • 在组件销毁时清理Observer,防止内存泄漏

3. 非SvelteKit项目适配方案

如果使用自定义路由或其他路由库,替换路由监听部分代码:

// 监听浏览器popstate事件
const handleRouteChange = () => {
    setTimeout(() => {
        getTableOfContents()
        setupObserver()
        showToc = false
    }, 100)
}

window.addEventListener('popstate', handleRouteChange)

// 组件销毁时移除监听
onDestroy(() => {
    window.removeEventListener('popstate', handleRouteChange)
    // 清理Observer逻辑...
})

内容的提问来源于stack exchange,提问作者Лео

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:17:07