VuePress Toc 组件实现方案
记 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 标签。
© 商业转载请联系站长获得授权;
非商业转载请注明文章来源及链接。
评论