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

推荐订阅源

D
DataBreaches.Net
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
Y
Y Combinator Blog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
量子位
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
C
Cybersecurity and Infrastructure Security Agency CISA
L
Lohrmann on Cybersecurity
L
LINUX DO - 最新话题
The Register - Security
The Register - Security
T
Tailwind CSS Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
T
Troy Hunt's Blog
Stack Overflow Blog
Stack Overflow Blog
Cloudbric
Cloudbric
S
Secure Thoughts
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
L
LangChain Blog
Recorded Future
Recorded Future
小众软件
小众软件
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Tor Project blog
人人都是产品经理
人人都是产品经理
F
Full Disclosure
O
OpenAI News
Webroot Blog
Webroot Blog
A
Arctic Wolf
TaoSecurity Blog
TaoSecurity Blog
P
Privacy & Cybersecurity Law Blog
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
雷峰网
雷峰网
Microsoft Security Blog
Microsoft Security Blog
H
Heimdal Security Blog
B
Blog RSS Feed
Vercel News
Vercel News

无数

几何布朗运动下股票价格涨超/跌超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 标签。

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