博客目录的改进
2025 年 07 月 01 日 • 关于本站
博客目录存在的一些BUG
之前的博客目录存在一些BUG,如同名标题跳转问题以及正则解析标题可能会将代码块注释也解析成标题。
同名标题跳转问题
这个使用markdown-it-anchor
插件在标题渲染成html的时候添加一个唯一id
,插件内置唯一id
策略当slugify
的值相同时候,则返回slugify-{idx}
vue
复制代码
.use(anchor, { slugify: s => s.trim().toLowerCase().replace(/\s+/g, '-') }) // 标题锚点支持
�所以另一边解析目录结构的时候也要配合解析出id
来
vue
复制代码
const usedIds = new Set()
for (const line of lines) {
const match = line.match(/^(#{1,6})\s+(.*)/)
if (match) {
const level = match[1].length
const text = match[2].trim()
const slug = text.toLowerCase().replace(/\s+/g, '-')
let uniqueSlug = slug
let i = 1
while (usedIds.has(uniqueSlug)) {
uniqueSlug = `${slug}-${i++}`
}
usedIds.add(uniqueSlug)
const node : TocNode = { level, text, id: uniqueSlug, children: [] }
这样之前用textcotent
来做Map
的Key
就可以换成id
了。
解析标题问题
直接使用markdown-it
解析AST
,避免正则再去判断#号开头。
vue
复制代码
const md = new MarkdownIt()
// 生成 TOC 树结构
const generateTocTree = (markdown : string) : TocNode[] => {
const tokens = md.parse(markdown, {})
const toc: TocNode[] = []
const stack: TocNode[] = []
const usedIds = new Set<string>()
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token.type === 'heading_open') {
const level = parseInt(token.tag.slice(1)) // e.g. 'h2' -> 2
const inlineToken = tokens[i + 1]
if (inlineToken && inlineToken.type === 'inline') {
const text = inlineToken.content.trim()
let slug = text.toLowerCase().replace(/\s+/g, '-')
let uniqueSlug = slug
let j = 1
while (usedIds.has(uniqueSlug)) {
uniqueSlug = `${slug}-${j++}`
}
usedIds.add(uniqueSlug)
const node: TocNode = { level, text, id: uniqueSlug, children: [] }
while (stack.length && stack[stack.length - 1].level >= level) {
stack.pop()
}
if (stack.length === 0) {
toc.push(node)
} else {
stack[stack.length - 1].children.push(node)
}
stack.push(node)
}
}
}
return toc
}
留言 (0)