开发预览 更新于 2026-05-10

Toc 目录锚点

文章侧边目录,按 depth 缩进,可手动指定或自动(IntersectionObserver scroll-spy)跟随当前阅读位置。

基础用法

items 是一个数组,每项至少有 idlabelactiveId 控制当前高亮项;点击会平滑滚动并更新地址栏 hash。

背景 视口
src/App.vue
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';

const items: TocItem[] = [
  { id: 'intro', label: '介绍', depth: 1 },
  { id: 'install', label: '安装', depth: 1 },
  { id: 'usage', label: '基本用法', depth: 1 },
  { id: 'theme', label: '主题', depth: 1 },
];
</script>
<template>
  <div style="max-width: 220px;">
    <CfToc :items="items" :active-id="'install'" title="本页目录" />
  </div>
</template>
<script setup>
import { CfToc } from '@chufix-design/vue';

const items= [
  { id: 'intro', label: '介绍', depth: 1 },
  { id: 'install', label: '安装', depth: 1 },
  { id: 'usage', label: '基本用法', depth: 1 },
  { id: 'theme', label: '主题', depth: 1 },
];
</script>
<template>
  <div style="max-width: 220px;">
    <CfToc :items="items" :active-id="'install'" title="本页目录" />
  </div>
</template>
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items: TocItem[] = [
    { id: 'intro', label: '介绍', depth: 1 },
    { id: 'install', label: '安装', depth: 1 },
    { id: 'usage', label: '基本用法', depth: 1 },
    { id: 'theme', label: '主题', depth: 1 },
  ];
  return (
    <>
      <div style={{ maxWidth: 220 }}>
          <CfToc items={items} activeId={'install'} title="本页目录" />
        </div>
    </>
  );
}
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items= [
    { id: 'intro', label: '介绍', depth: 1 },
    { id: 'install', label: '安装', depth: 1 },
    { id: 'usage', label: '基本用法', depth: 1 },
    { id: 'theme', label: '主题', depth: 1 },
  ];
  return (
    <>
      <div style={{ maxWidth: 220 }}>
          <CfToc items={items} activeId={'install'} title="本页目录" />
        </div>
    </>
  );
}

嵌套缩进

depth: 1..6 控制缩进层级。同一份 items 数组只要 depth 配对正确,就自然形成嵌套缩进。

src/App.vue
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';

const items: TocItem[] = [
  { id: 'intro', label: '介绍', depth: 1 },
  { id: 'install', label: '安装', depth: 1 },
  { id: 'install-vue', label: 'Vue 3', depth: 2 },
  { id: 'install-react', label: 'React 18', depth: 2 },
  { id: 'install-vanilla', label: '纯 HTML', depth: 2 },
  { id: 'usage', label: '基本用法', depth: 1 },
  { id: 'theme', label: '主题与 Tokens', depth: 1 },
  { id: 'theme-dark', label: 'dark-cool', depth: 2 },
  { id: 'theme-warm', label: 'dark-warm', depth: 2 },
  { id: 'theme-detail', label: '颜色细节', depth: 3 },
];
</script>
<template>
  <div style="max-width: 220px;">
    <CfToc :items="items" :active-id="'install-vue'" title="本页目录" />
  </div>
</template>
<script setup>
import { CfToc } from '@chufix-design/vue';

const items= [
  { id: 'intro', label: '介绍', depth: 1 },
  { id: 'install', label: '安装', depth: 1 },
  { id: 'install-vue', label: 'Vue 3', depth: 2 },
  { id: 'install-react', label: 'React 18', depth: 2 },
  { id: 'install-vanilla', label: '纯 HTML', depth: 2 },
  { id: 'usage', label: '基本用法', depth: 1 },
  { id: 'theme', label: '主题与 Tokens', depth: 1 },
  { id: 'theme-dark', label: 'dark-cool', depth: 2 },
  { id: 'theme-warm', label: 'dark-warm', depth: 2 },
  { id: 'theme-detail', label: '颜色细节', depth: 3 },
];
</script>
<template>
  <div style="max-width: 220px;">
    <CfToc :items="items" :active-id="'install-vue'" title="本页目录" />
  </div>
</template>
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items: TocItem[] = [
    { id: 'intro', label: '介绍', depth: 1 },
    { id: 'install', label: '安装', depth: 1 },
    { id: 'install-vue', label: 'Vue 3', depth: 2 },
    { id: 'install-react', label: 'React 18', depth: 2 },
    { id: 'install-vanilla', label: '纯 HTML', depth: 2 },
    { id: 'usage', label: '基本用法', depth: 1 },
    { id: 'theme', label: '主题与 Tokens', depth: 1 },
    { id: 'theme-dark', label: 'dark-cool', depth: 2 },
    { id: 'theme-warm', label: 'dark-warm', depth: 2 },
    { id: 'theme-detail', label: '颜色细节', depth: 3 },
  ];
  return (
    <>
      <div style={{ maxWidth: 220 }}>
          <CfToc items={items} activeId={'install-vue'} title="本页目录" />
        </div>
    </>
  );
}
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items= [
    { id: 'intro', label: '介绍', depth: 1 },
    { id: 'install', label: '安装', depth: 1 },
    { id: 'install-vue', label: 'Vue 3', depth: 2 },
    { id: 'install-react', label: 'React 18', depth: 2 },
    { id: 'install-vanilla', label: '纯 HTML', depth: 2 },
    { id: 'usage', label: '基本用法', depth: 1 },
    { id: 'theme', label: '主题与 Tokens', depth: 1 },
    { id: 'theme-dark', label: 'dark-cool', depth: 2 },
    { id: 'theme-warm', label: 'dark-warm', depth: 2 },
    { id: 'theme-detail', label: '颜色细节', depth: 3 },
  ];
  return (
    <>
      <div style={{ maxWidth: 220 }}>
          <CfToc items={items} activeId={'install-vue'} title="本页目录" />
        </div>
    </>
  );
}

maxDepth 限制

maxDepth 决定渲染的最深层级 — 比如博客侧栏只想显示 H1 / H2 时,传 maxDepth={2},更深的标题不渲染。

src/App.vue
<script setup lang="ts">
import { CfToc, type TocItem } from '@chufix-design/vue';

const items: TocItem[] = [
  { id: 'a', label: '一级标题', depth: 1 },
  { id: 'a1', label: '二级标题', depth: 2 },
  { id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
  { id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
];
</script>
<template>
  <div style="display: flex; gap: 16px;">
    <div style="max-width: 200px;">
      <CfToc :items="items" :max-depth="2" />
    </div>
    <div style="max-width: 240px;">
      <CfToc :items="items" :max-depth="4" />
    </div>
  </div>
</template>
<script setup>
import { CfToc } from '@chufix-design/vue';

const items= [
  { id: 'a', label: '一级标题', depth: 1 },
  { id: 'a1', label: '二级标题', depth: 2 },
  { id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
  { id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
];
</script>
<template>
  <div style="display: flex; gap: 16px;">
    <div style="max-width: 200px;">
      <CfToc :items="items" :max-depth="2" />
    </div>
    <div style="max-width: 240px;">
      <CfToc :items="items" :max-depth="4" />
    </div>
  </div>
</template>
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items: TocItem[] = [
    { id: 'a', label: '一级标题', depth: 1 },
    { id: 'a1', label: '二级标题', depth: 2 },
    { id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
    { id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
  ];
  return (
    <>
      <div style={{ display: "flex", gap: 16 }}>
          <div style={{ maxWidth: 200 }}>
            <CfToc items={items} maxDepth={2} />
          </div>
          <div style={{ maxWidth: 240 }}>
            <CfToc items={items} maxDepth={4} />
          </div>
        </div>
    </>
  );
}
import { CfToc } from '@chufix-design/react';

export default function Demo() {
  const items= [
    { id: 'a', label: '一级标题', depth: 1 },
    { id: 'a1', label: '二级标题', depth: 2 },
    { id: 'a1a', label: '三级 — 仅 maxDepth=3 显示', depth: 3 },
    { id: 'a1ai', label: '四级 — 仅 maxDepth>=4 显示', depth: 4 },
  ];
  return (
    <>
      <div style={{ display: "flex", gap: 16 }}>
          <div style={{ maxWidth: 200 }}>
            <CfToc items={items} maxDepth={2} />
          </div>
          <div style={{ maxWidth: 240 }}>
            <CfToc items={items} maxDepth={4} />
          </div>
        </div>
    </>
  );
}

自动滚动跟随(scroll-spy)

autoSpy: true 让组件自动监听文章里 iditems 对应的标题,根据可视范围自动切换 active。scrollRoot 指定监听容器;省略时监听 viewport。

<!-- Vue -->
<CfToc :items="items" auto-spy />
<CfToc :items="items" auto-spy scroll-root=".article-body" />
{/* React */}
<CfToc items={items} autoSpy />
<CfToc items={items} autoSpy scrollRoot=".article-body" />

API

属性类型默认值说明
itemsTocItem[][]目录条目
autoSpybooleanfalse启用 IntersectionObserver 自动跟随
scrollRootstring监听容器选择器;省略则使用 viewport
activeIdstring | nullnull受控当前 id
defaultActiveId (React)string | nullnull非受控初始值
titlestring | ReactNode顶部小标题
maxDepthnumber6渲染的最大深度

TocItem{ id, label, depth? }。事件:update:activeId(React 端:onActiveIdChange)。

反馈与讨论

Toc 目录锚点 的讨论

0
0 / 600
正在加载评论...