Vue3 渲染 markdown文件,并生成锚点目录树(toc)

最近想要自己手写一个博客,一开始还挺好,可是当我做锚点目录的时候,却无从下手,经过几天的尝试,也找到了比较简单的解决方法。

安装 markedjs

之所以用marked,而不是markdown-it,是因为markdown-it 无法渲染内容过长的md文件内容。

npm install marked

Vue 中使用 marked 渲染 md文件

<template>
    <div v-html="content"></div>
</template>

<script setup>
import { ref, nextTick } from 'vue'
// 配置 mared
import { marked } from 'marked'
const render = new marked.Renderer()
marked.setOptions({
  renderer: render, // 这是必填项
  gfm: true, // 启动类似于Github样式的Markdown语法
  pedantic: false, // 只解析符合Markdwon定义的,不修正Markdown的错误
  sanitize: true // 原始输出,忽略HTML标签(关闭后,可直接渲染HTML标签)
})
// 1. 文章内容
const content = ref('')
const initContent = () => {
  content.value = marked(/* 渲染的md文本,例如 '# hello\r\nworld!' */)
}
nextTick(() => {
  initContent()
})
</script>

 当内容为 '# hello\r\nworld!' 时,渲染的页面如图所示,同时注意到 marked 自动为标题设置id属性

 生成 toc 目录树

<template>
  <!-- 这些标题是为了演示用的,实际是用 marked 渲染的 -->
  <h1 id="item-1">item-1</h1>
  <h2 id="item-1.1">item-1.1</h2>
  <h3 id="item-1.1.1">item-1.1.1</h3>
  <h1 id="item-2">item-2</h1>
  <h2 id="item-2.1">item-2.1</h2>
  <h2 id="item-2.2">item-2.2</h2>
  <h3 id="item-2.2.1">item-2.2.1</h3>
  <h3 id="item-2.2.2">item-2.2.2</h3>
  <ul>
      <!-- 这里为了设置各级标题的不同样式,添加了类,h1标签类为item-1,h2标签类为item-2 -->
      <li
        v-for="item in tocData"
        :key="item.id"
        :class="`item-${item.tagName.charAt(1)}`"
      >
          {{item.id}}
      </li>
  </ul>
</template>

<script setup>
import { ref, nextTick, watch } from 'vue'
// TOC 目录
function initToc() {
  // 获取所有h1 h2 h3 标签
  let all_headings = document.querySelectorAll('h1, h2, h3')
  return all_headings
}
// 存放 toc 目录数据
const tocData = ref(null)

nextTick(() => {
  tocData.value = initToc()
})
</script>

<style>
ul {
  border: 1px solid #000;
}
ul li {
  list-style: none;
}
.item-1 {
  font-weight: 700;
  padding-left: 0;
}
.item-2 {
  padding-left: 20px;
}
.item-3 {
  padding-left: 40px;
}
</style>

 生成的页面样式如下

 当然,此时是无法跳转的,如果你把li标签改为a标签,由于vue可能使用 hash路由模式,会产生冲突,那怎么呢?很简单,既然我们知道了标题对应内容的id值,那么我们就可以利用此 id跳转到对应的内容位置。

<template>
  <-- 定义 anchor 函数 -->
  <li
    v-for="item in tocData"
    :key="item.id"
    :class="`item-${item.tagName.charAt(1)}`"
    @click="anchor(item.id)"
  >
      {{item.id}}
  </li>
</template>

<script setup>
  const anchor = (id) => {
    let anchorElement = document.getElementById(anchorId)
    if (anchorElement) {
      anchorElement.scrollIntoView()
    }
  }
</script>

注意:marked 添加的id属性一定不会包含空格和 '.' 等特殊符号,要么被移除,要么被自动转换成 '-' 符号。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值