vue实现目录索引
实现目录索引的基本思路
在Vue中实现目录索引通常涉及动态生成目录结构,并实现点击跳转功能。核心是通过解析页面内容(如标题标签)生成目录,利用Vue的响应式特性更新目录状态。
解析标题生成目录结构
使用document.querySelectorAll获取所有标题元素(如h1-h6),提取文本和层级信息。通过递归或循环构建嵌套的目录树结构:
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
const toc = [];
let lastLevel = 0;
headings.forEach((heading) => {
const level = parseInt(heading.tagName.substring(1));
const item = {
id: heading.id || `${heading.textContent}-${Math.random().toString(36).substr(2, 9)}`,
text: heading.textContent,
level,
children: []
};
// 根据层级关系构建树形结构
if (level > lastLevel && toc.length > 0) {
toc[toc.length - 1].children.push(item);
} else {
toc.push(item);
}
lastLevel = level;
});
实现目录组件
创建可复用的Vue组件,接收目录数据并渲染为可点击的列表。使用v-for动态生成目录项,通过v-bind:class高亮当前阅读位置:
<template>
<div class="toc-container">
<ul>
<li
v-for="item in tocData"
:key="item.id"
:class="{ 'active': activeId === item.id }"
@click="scrollTo(item.id)"
>
{{ item.text }}
<ul v-if="item.children.length > 0">
<!-- 递归渲染子目录 -->
<toc-item
v-for="child in item.children"
:key="child.id"
:item="child"
:activeId="activeId"
@scrollTo="scrollTo"
/>
</ul>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['tocData', 'activeId'],
methods: {
scrollTo(id) {
document.getElementById(id).scrollIntoView({ behavior: 'smooth' });
this.$emit('update:activeId', id);
}
}
};
</script>
监听滚动位置高亮目录项
通过IntersectionObserver或滚动事件监听当前可视区域的标题,更新activeId实现高亮效果:
export default {
data() {
return {
activeId: ''
};
},
mounted() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.activeId = entry.target.id;
}
});
},
{ threshold: 0.5 }
);
document.querySelectorAll('h1, h2, h3').forEach(heading => {
observer.observe(heading);
});
}
};
样式优化
为目录添加基础样式,确保层级清晰且可交互:
.toc-container {
position: fixed;
top: 20px;
left: 20px;
max-width: 250px;
}
.toc-container ul {
list-style: none;
padding-left: 1em;
}
.toc-container li {
cursor: pointer;
margin: 5px 0;
padding: 3px 8px;
border-radius: 4px;
}
.toc-container li.active {
background-color: #e0f7fa;
font-weight: bold;
}
.toc-container li:hover {
background-color: #f5f5f5;
}
动态锚点生成
若标题缺少id属性,需在组件挂载时动态生成唯一锚点:
mounted() {
document.querySelectorAll('h1, h2, h3').forEach(heading => {
if (!heading.id) {
heading.id = heading.textContent
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w-]/g, '');
}
});
}
响应式目录更新
当页面内容动态变化时(如通过API加载),需重新解析目录并更新组件:
watch: {
content() {
this.$nextTick(() => {
this.tocData = this.generateToc();
});
}
}






