vue如何实现目录组件
实现目录组件的基本思路
在Vue中实现目录组件,通常需要基于页面内容的结构动态生成目录树,并支持点击跳转。核心步骤包括解析标题元素、生成目录结构、高亮当前阅读位置。
解析标题元素
使用document.querySelectorAll获取页面中所有标题标签(如h1、h2等),过滤出需要生成目录的部分。通过遍历这些元素,提取文本内容和id(若无则动态生成)。

const headings = Array.from(document.querySelectorAll('h1, h2, h3'))
.filter(el => el.textContent.trim())
.map(el => ({
id: el.id || generateId(el.textContent),
text: el.textContent,
level: parseInt(el.tagName.substring(1))
}));
生成目录结构
将扁平化的标题数组转换为嵌套的树形结构,通常通过比较层级关系(如h2是h1的子项)实现递归分组。

function buildTree(items, parentLevel = 1) {
const result = [];
while (items.length) {
const item = items[0];
if (item.level === parentLevel) {
result.push({ ...item, children: [] });
items.shift();
} else if (item.level > parentLevel) {
result[result.length - 1].children = buildTree(items, item.level);
} else {
break;
}
}
return result;
}
高亮当前阅读位置
监听滚动事件,通过IntersectionObserver或getBoundingClientRect判断当前视口内哪个标题最接近顶部,动态更新目录高亮状态。
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
activeId.value = entry.target.id;
}
});
}, { threshold: 1 });
headings.forEach(heading => {
observer.observe(document.getElementById(heading.id));
});
完整组件示例
<template>
<div class="toc">
<ul>
<li v-for="item in tree" :key="item.id" :class="{ active: activeId === item.id }">
<a :href="`#${item.id}`">{{ item.text }}</a>
<ul v-if="item.children.length">
<li v-for="child in item.children" :key="child.id" :class="{ active: activeId === child.id }">
<a :href="`#${child.id}`">{{ child.text }}</a>
</li>
</ul>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const activeId = ref('');
const tree = ref([]);
onMounted(() => {
const headings = Array.from(document.querySelectorAll('h1, h2, h3'))
.filter(el => el.textContent.trim())
.map(el => ({
id: el.id || generateId(el.textContent),
text: el.textContent,
level: parseInt(el.tagName.substring(1))
}));
tree.value = buildTree(headings);
});
</script>
样式优化建议
为目录添加缩进和交互效果,提升用户体验:
.toc ul {
list-style: none;
padding-left: 1em;
}
.toc li.active > a {
color: #42b983;
font-weight: bold;
}
.toc a:hover {
text-decoration: underline;
}
注意事项
- 动态生成的
id需确保唯一性,可通过哈希函数或时间戳实现。 - 对于SPA应用,需在路由变化时重新生成目录。
- 复杂场景可考虑使用现成库如
vue-toc或markdown-it解析内容。





