惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
Hugging Face - Blog
Hugging Face - Blog
L
LINUX DO - 热门话题
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
D
Docker
C
Cyber Attacks, Cyber Crime and Cyber Security
MyScale Blog
MyScale Blog
P
Palo Alto Networks Blog
T
Tenable Blog
P
Privacy International News Feed
Google DeepMind News
Google DeepMind News
小众软件
小众软件
Cisco Talos Blog
Cisco Talos Blog
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
T
Threat Research - Cisco Blogs
NISL@THU
NISL@THU
The Hacker News
The Hacker News
Project Zero
Project Zero
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threatpost
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
The Register - Security
The Register - Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Vercel News
Vercel News
D
Darknet – Hacking Tools, Hacker News & Cyber Security
MongoDB | Blog
MongoDB | Blog
U
Unit 42
Scott Helme
Scott Helme
A
About on SuperTechFans
WordPress大学
WordPress大学
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
G
GRAHAM CLULEY
Latest news
Latest news
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security

无数

几何布朗运动下股票价格涨超/跌超50%概率计算步骤 两款软件助你方便调节 Windows 外接显示器亮度 iBasso DC07Pro 对比 EPZ TP55 从 Apple watch S4 升级到 S10 的使用体验 自己动手给 FiiO Q5s Type-C 版本更换电池 2025年9月底,265K+5080装机历程及体验 HomeAssistant 中使用 Vue 构建UI面板的简易方式 推倒重构博客UI的要点记录 基于 MapBox 构建 VuePress 的旅行地图
VuePress Toc 组件实现方案
晴和君 · 2025-03-06 · via 无数

记 VuePress RC20 更新后,官方 toc 插件报错替代方案

发布于

VuePress Toc 组件实现方案

近期 VuePress V2 更新到 RC20版本后,构建时会产生如下报错:

 Initializing and preparing data - done in 3.53s
 Compiling with vite - done in 4.27s
 Rendering 78 pages - failed in 59ms
error Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@internal/routes' imported from C:\Users\Admin\WebstormProjects\WuShu\node_modules\@vuepress\client\dist\chunk-TET3OVHF.js
    at new NodeError (node:internal/errors:406:5)
    at packageResolve (node:internal/modules/esm/resolve:789:9)
    at moduleResolve (node:internal/modules/esm/resolve:838:20)
    at defaultResolve (node:internal/modules/esm/resolve:1043:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:383:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:352:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:228:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
    at link (node:internal/modules/esm/module_job:84:36)

经过逐一排查,发现问题在于这个@vuepress/[email protected]这个toc插件中。这个插件在本站提供了文章页的文章目录导航的功能。 虽然预期会很快修复,但是我不太喜欢这种不确定性,另外感觉实现并不复杂,于是着手进行改造替换。 更新:前一天已发布@vuepress/[email protected],估计是对这个问题进行了解决,但我没有重新进行测试。

构建思路

因为之前已经根据 toc 插件生成的目录树结构,构建了目录指示条,以及对样式、activeHeaderLinksPlugin 插件进行了优化,所以本次进行替换尽量保持和原来的 html 结构保持一致。 同时,因为本站文章小标题只采用二、三级标题,因此只简化生成此两类标题目录,最终模板部分代码如下:

<nav class="vuepress-toc">
    <ul class="vuepress-toc-list">
        <li
                v-for="item in headers"
                :key="item.slug"
                class="vuepress-toc-item"
        >
            <a
                    class="route-link vuepress-toc-link"
                    :class="{'asideActive': hashPos === item.link, 'active': hasActiveChild(item)}"
                    :href="item.link"
                    :aria-label="item.title"
            >
                {{ item.title }}
            </a>
            <ul class="vuepress-toc-list"
                v-if="hasChildren(item)"
            >
                <li v-for="child in item.children" :key="item.slug" class="vuepress-toc-item">
                    <a
                            class="route-link vuepress-toc-link"
                            :class="{'asideActive': hashPos === child.link, 'active': hashPos === child.link}"
                            :href="child.link"
                            :aria-label="child.title"
                    >
                        {{ child.title }}
                    </a>
                </li>
            </ul>
        </li>
    </ul>
</nav>

javascript 代码如下:

import {useRoute} from "vue-router";
import {ref, watch} from "vue";

// 当前激活项目,以及用来判断是否有子项目被激活
const route = useRoute();
let hashPos = ref()
watch(
    () => route.hash,
    (newv, oldv) => {
        hashPos.value = newv;
    }, {immediate: true}
);

// 用来传入文章headers信息
defineProps({
    headers: {
        type: Array,
        required: true
    }
})

// 判断是否有子项目(三级标题)
const hasChildren = (item) => {
    return item.children?.length > 0
}

// 递归检查是否存在激活子项
const hasActiveChild = (item) => {
    const checkChildren = (children) => {
        return children.some(child => {
            return child.link === hashPos.value ||  // 当前子项激活
                (child.children && checkChildren(child.children))  // 递归检查深层子项
        })
    }
    return item.children ? checkChildren(item.children) : false
}

使用方式

在需要使用的页面导入该组件:

import Toc from "../components/Toc.vue";

并在模板中使用即可:

<ClientOnly>
    <Toc :headers="PageData.headers"/>
</ClientOnly>

因为涉及到内容更新,为了避免报 app-DoG9RpcD.js:15 [Vue warn]: Hydration class mismatch 错误,可以在外围加上 ClientOnly 标签。

© 商业转载请联系站长获得授权;
非商业转载请注明文章来源及链接。