You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中如何生成独立的Markdown格式目录?

独立生成Markdown目录的优化方案

你的现有函数可以正常工作,但基于Markdown AST(抽象语法树)的实现会更稳定、易维护,且能避免依赖渲染组件的结构细节。以下是两种利用remark生态工具的优化方案:

方案一:快速提取标准目录

借助unified、remark-parse、remark-slug和mdast-util-toc工具,直接解析Markdown并提取目录结构,无需手动处理层级和ID生成。

步骤1:安装依赖

npm install unified remark-parse remark-slug mdast-util-toc mdast-util-to-markdown

步骤2:实现提取函数

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkSlug from 'remark-slug'
import { toc } from 'mdast-util-toc'
import { toMarkdown } from 'mdast-util-to-markdown'
import fs from 'fs'
import matter from 'gray-matter'

const getMarkdownTOC = (slug) => {
  const folder = process.env.PATH_ARTICULOS
  const file = `${folder}${slug}.md`
  const content = fs.readFileSync(file, 'utf8')
  const matterResult = matter(content)

  // 解析Markdown为AST,并自动生成标题ID
  const tree = unified()
    .use(remarkParse)
    .use(remarkSlug)
    .parse(matterResult.content)

  // 提取目录结构,不生成目录标题
  const tocResult = toc(tree, {
    heading: false,
    maxDepth: 6 // 可按需调整要包含的标题层级
  })

  // 无标题时返回空字符串
  if (!tocResult.map) return ''

  // 将目录AST转换为Markdown字符串
  return toMarkdown(tocResult.map)
}

方案二:自定义目录格式

如果需要完全自定义目录的列表符号、缩进规则或处理富文本标题,可以用unist-util-visit手动遍历AST节点:

步骤1:安装额外依赖

npm install unified remark-parse remark-slug unist-util-visit

步骤2:实现自定义提取函数

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkSlug from 'remark-slug'
import { visit } from 'unist-util-visit'
import fs from 'fs'
import matter from 'gray-matter'

const getMarkdownTOC = (slug) => {
  const folder = process.env.PATH_ARTICULOS
  const file = `${folder}${slug}.md`
  const content = fs.readFileSync(file, 'utf8')
  const matterResult = matter(content)

  const tree = unified()
    .use(remarkParse)
    .use(remarkSlug)
    .parse(matterResult.content)

  const tocItems = []

  // 遍历所有标题节点,收集层级、文本和ID
  visit(tree, 'heading', (node) => {
    const level = node.depth
    // 处理标题中的富文本节点(如加粗、斜体),提取纯文本
    const title = node.children.map(child => {
      if (child.type === 'text') return child.value
      if (['strong', 'emphasis'].includes(child.type)) return child.children[0].value
      // 可根据需求扩展其他节点类型的处理
      return ''
    }).join('')
    // 获取由remark-slug生成的标题ID
    const id = node.data?.hProperties?.id

    if (id) tocItems.push({ level, title, id })
  })

  // 生成带缩进的Markdown列表
  let toc = ''
  const indent = '  '
  tocItems.forEach(item => {
    toc += `${indent.repeat(item.level - 1)}- [${item.title}](#${item.id})\n`
  })

  return toc.trim()
}

方案优势

  • 相比你原有的遍历渲染组件结构的方式,基于AST的实现更稳定,不依赖编译器的输出结构,维护性更强。
  • 利用remark生态工具,无需手动处理层级缩进、ID生成等细节,减少重复代码。
  • 两种方案都能独立生成目录内容,完全脱离原文档,满足你在任意位置渲染的需求。

内容的提问来源于stack exchange,提问作者Joan Lara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:10:51