• 记一次使用vue-markdown在vue中解析markdown格式文件,并自动生成目录大纲


    先上效果图

    如图所示,在网页中,能直接解析markdown文档,并且生成目录大纲,也支持点击目录标题跳转到对应栏目中,下面就来讲讲是如何实现此功能的。

    1、下载vue-markdown

    yarn add vue-markdown

    2、在页面中渲染markdown文件

    1. <template>
    2. <div>
    3. <VueMarkdown
    4. class="api-content"
    5. :source="markdownContent"
    6. id="content"
    7. />
    8. div>
    9. template>
    10. <script>
    11. import VueMarkdown from "vue-markdown";
    12. export default {
    13. components: {
    14. VueMarkdown,
    15. },
    16. data() {
    17. return {
    18. markdownContent: "",
    19. };
    20. },
    21. mounted() {
    22. this.loadMarkdownFile();
    23. },
    24. methods: {
    25. async loadMarkdownFile() {
    26. try {
    27. // api.md文件存放在根目录下的public文件夹中
    28. const response = await fetch("/api.md");
    29. const markdownText = await response.text();
    30. this.markdownContent = markdownText;
    31. } catch (error) {
    32. console.error("Failed to load the Markdown file:", error);
    33. }
    34. },
    35. },
    36. };
    37. script>

    此时,打开浏览器查看,页面中已经正常渲染markdown文件了。

    3、生成目录大纲

    现在,我们需要有目录大纲方便我们查看文档。我的思路是,首先拿到markdown文件的html结构,然后找到所有H1-H5的标题标签,并给这些标签设置id,生成一个新数组,通过这个数组生成目录结构,说干就干。

    1. //html部分
    2. class="api-tree" id="tree">
    3. <el-tree
    4. :data="tree"
    5. :default-expand-all="true"
    6. @node-click="handleNodeClick"
    7. >el-tree>
  • // js部分
  • catalogTree() {
  • const content = document.getElementById("content").children;
  • var arr = [];
  • let currentHightestLevel;
  • let parentId;
  • let index = 0;
  • for (let i = 0; i < content.length; i++) {
  • let header = content[i].localName;
  • if (/\b[h][0-9]\b/.test(header)) {
  • let ele = $(content[i]);
  • let name = ele.text();
  • // 设置id
  • ele.attr("id", i);
  • let id = i;
  • if (index === 0 || header <= currentHightestLevel) {
  • currentHightestLevel = header;
  • parentId = id;
  • }
  • arr.push({
  • id: id,
  • label: name,
  • parentId: parentId == id ? "0" : parentId,
  • });
  • index++;
  • }
  • }
  • const tree = [];
  • arr.forEach((item) => {
  • if (item.parentId === "0") {
  • tree.push(this.convertArrayToTree(arr, item));
  • }
  • });
  • this.tree = tree;
  • },
  • convertArrayToTree(arr, node) {
  • for (let i = 0; i < arr.length; i++) {
  • if (arr[i].parentId === node.id) {
  • const res = this.convertArrayToTree(arr, arr[i]);
  • if (node.children) {
  • node.children.push(res);
  • } else {
  • node.children = [res];
  • }
  • }
  • }
  • return node;
  • },
  • handleNodeClick(data) {
  • let anchorElement = document.getElementById(data.id);
  • if (anchorElement) {
  • anchorElement.scrollIntoView({
  • behavior: "smooth",
  • block: "end",
  • });
  • }
  • },
  • 4、大功告成,最后,附上全部代码,带css样式

    1. <template>
    2. <div class="page-api" id="myElement">
    3. <div class="api-tree" id="tree">
    4. <el-tree
    5. :data="tree"
    6. :default-expand-all="true"
    7. @node-click="handleNodeClick"
    8. >el-tree>
    9. div>
    10. <VueMarkdown
    11. class="api-content"
    12. :source="markdownContent"
    13. id="content"
    14. />
    15. div>
    16. template>
    17. <script>
    18. import VueMarkdown from "vue-markdown";
    19. import $ from "jquery";
    20. export default {
    21. components: {
    22. VueMarkdown,
    23. },
    24. data() {
    25. return {
    26. markdownContent: "",
    27. tree: [],
    28. };
    29. },
    30. mounted() {
    31. this.loadMarkdownFile();
    32. },
    33. methods: {
    34. async loadMarkdownFile() {
    35. try {
    36. const response = await fetch("/api.md");
    37. const markdownText = await response.text();
    38. this.markdownContent = markdownText;
    39. this.$nextTick(() => {
    40. this.catalogTree();
    41. });
    42. } catch (error) {
    43. console.error("Failed to load the Markdown file:", error);
    44. }
    45. },
    46. catalogTree() {
    47. const content = document.getElementById("content").children;
    48. var arr = [];
    49. let currentHightestLevel;
    50. let parentId;
    51. let index = 0;
    52. for (let i = 0; i < content.length; i++) {
    53. let header = content[i].localName;
    54. if (/\b[h][0-9]\b/.test(header)) {
    55. let ele = $(content[i]);
    56. let name = ele.text();
    57. // 设置id
    58. ele.attr("id", i);
    59. // let id = ele.children("a").attr("id");
    60. let id = i;
    61. if (index === 0 || header <= currentHightestLevel) {
    62. currentHightestLevel = header;
    63. parentId = id;
    64. }
    65. arr.push({
    66. id: id,
    67. label: name,
    68. parentId: parentId == id ? "0" : parentId,
    69. });
    70. index++;
    71. }
    72. }
    73. const tree = [];
    74. arr.forEach((item) => {
    75. if (item.parentId === "0") {
    76. tree.push(this.convertArrayToTree(arr, item));
    77. }
    78. });
    79. this.tree = tree;
    80. },
    81. convertArrayToTree(arr, node) {
    82. for (let i = 0; i < arr.length; i++) {
    83. if (arr[i].parentId === node.id) {
    84. const res = this.convertArrayToTree(arr, arr[i]);
    85. if (node.children) {
    86. node.children.push(res);
    87. } else {
    88. node.children = [res];
    89. }
    90. }
    91. }
    92. return node;
    93. },
    94. handleNodeClick(data) {
    95. let anchorElement = document.getElementById(data.id);
    96. let scrollPosition = anchorElement.offsetTop - 20;
    97. let myElement = document.getElementById("myElement");
    98. myElement.scrollTo({
    99. left: 0,
    100. top: scrollPosition,
    101. behavior: "smooth",
    102. });
    103. // if (anchorElement) {
    104. // anchorElement.scrollIntoView({
    105. // behavior: "smooth",
    106. // block: "end",
    107. // });
    108. // }
    109. },
    110. },
    111. };
    112. script>
    113. <style lang="scss">
    114. .page-api {
    115. display: flex;
    116. height: 100%;
    117. overflow-y: scroll;
    118. .api-tree {
    119. position: fixed;
    120. left: 20px;
    121. top: 120px;
    122. width: 200px;
    123. height: calc(100% - 160px);
    124. overflow-y: scroll;
    125. z-index: 99;
    126. .el-tree {
    127. background: none;
    128. color: #fff;
    129. .el-tree-node:focus > .el-tree-node__content,
    130. .el-tree-node__content:hover {
    131. background: none;
    132. color: rgb(24, 144, 255);
    133. }
    134. }
    135. }
    136. .api-content {
    137. flex: 1;
    138. margin-left: 220px;
    139. padding: 0 30px;
    140. color: rgba(255, 255, 255, 0.75);
    141. h3 {
    142. margin-left: 25px;
    143. }
    144. code {
    145. border-radius: 2px;
    146. color: #e96900;
    147. margin: 0 2px;
    148. padding: 3px 5px;
    149. white-space: pre-wrap;
    150. }
    151. table {
    152. border-collapse: collapse;
    153. border-spacing: 0;
    154. th,
    155. td {
    156. border: 1px solid #ddd;
    157. padding: 6px 13px;
    158. margin: 0;
    159. }
    160. }
    161. pre {
    162. background: rgba(0, 0, 0, 0.7);
    163. padding: 20px 30px;
    164. }
    165. }
    166. }
    167. style>

  • 相关阅读:
    MySQL锁机制详解
    TCP协议
    mysql基本操作命令
    RHCSA认证考试---8.配置 autofs
    python dict字典如何增加若干相同重复的key键的item子项?
    java中的代理模式
    微擎模块 志汇同城微圈小程序10.9.5开源版
    【云原生】Minio on k8s 讲解与实战操作
    Scrum Master的职责
    微信公众号留言如何实时提醒
  • 原文地址:https://blog.csdn.net/weixin_41698051/article/details/133763128