1.下载包

 npm i github-markdown-css highlight.js marked marked-highlight mathjax --s

我本地node版本22.14.0

2.新建MathJax.js

在markdown.vue中引用

let isMathjaxConfig = false;
const initMathjaxConfig = () => {
    if (!window.MathJax) {
        return;
    }
    window.MathJax = {
        tex: {
            inlineMath: [
                ["$", "$"],
                ["\\(", "\\)"],
            ], // ⾏内公式选择符
            displayMath: [
                ["$$", "$$"],
                ["\\[", "\\]"],
            ], // 段内公式选择符
        },
        options: {
            skipHtmlTags: [
                "script",
                "noscript",
                "style",
                "textarea",
                "pre",
                "code",
                "a",
            ], // 避开某些标签
            ignoreHtmlClass: "tex2jax_ignore",
            processHtmlClass: "tex2jax_process",
        },
    };
    isMathjaxConfig = true;
};
const TypeSet = async function (elementId) {
    if (!window.MathJax) {
        return;
    }
    window.MathJax.startup.promise = window.MathJax.startup.promise
        .then(() => {
            return window.MathJax.typesetPromise();
        })
        .catch((err) => console.log("Typeset failed: " + err.message));
    return window.MathJax.startup.promise;
};
export default {
    isMathjaxConfig,
    initMathjaxConfig,
    TypeSet,
};

3.markdown.vue文件

<template>
  <div v-html="htmlContent" class="markdown-body"></div>
</template>

<script>
import {defineComponent, nextTick, onMounted, ref, watch} from 'vue';
import {Marked} from 'marked';
import {markedHighlight} from 'marked-highlight';
import hljs from 'highlight.js';
import 'highlight.js/styles/github.css';
import 'github-markdown-css/github-markdown-light.css';
import mathJax from "@/util/MathJax.js";
import "mathjax/es5/tex-svg"; // 使用 tex-svg.js

let doCopy = function (e) {
  const code = this.parentNode.parentNode.querySelector('code');
  navigator.clipboard
      .writeText(code.innerText)
      .then(() => {
        alert('复制成功');
      })
      .catch((error) => {
        alert('复制失败');
      });
}

export default defineComponent({
  name: 'DynamicMarkdown',
  props: {
    value: {
      type: String,
      required: true,
    },
  },
  setup(props) {
    const htmlContent = ref('');

    // 初始化 marked 实例
    const marked = new Marked(
        markedHighlight({
          async: false,
          langPrefix: 'language-',
          emptyLangClass: 'no-lang',
          highlight: (code, language) => {
            // 代码高亮自动识别语言的关键就在这里
            return hljs.highlightAuto(code, [language]).value;
          },
        })
    );

    // 增强代码块功能
    const enhanceCodeBlock = (content) => {
      return content.replace(/<pre><code/g, '<pre><div class="enhance"><div class="copyCode">复制</div></div><code');
    };

    // 复制功能绑定
    const bindCopyFunction = (el) => {
      const codeBlocks = el.querySelectorAll('pre');
      codeBlocks.forEach((codeBlock) => {
        const enhance = codeBlock.querySelector('.enhance');
        if (enhance) {
          const copyCode = enhance.querySelector('.copyCode');
          if (copyCode) {
            copyCode.removeEventListener('click', doCopy);
            copyCode.addEventListener('click', doCopy);
          }
        }
      });
    };

    // 解析 Markdown 内容
    const parseMarkdown = () => {
      htmlContent.value = props.value
      // 这里是关键,很多人就折在这了,时序上一定是先转公式,再取出来转Markdown,最后转代码
      nextTick(()=>{
        // 先转公式
        if (mathJax) {
          if (mathJax.isMathjaxConfig) {
            mathJax.initMathjaxConfig()
          }
          mathJax.TypeSet()
        }
        nextTick(()=>{
          htmlContent.value=document.querySelector('.markdown-body').innerHTML
          htmlContent.value = enhanceCodeBlock(marked.parse(htmlContent.value));
          nextTick(() => {
            const el = document.querySelector('.markdown-body');
            if (el) {
              bindCopyFunction(el);
            }
          });
        })
      })

    };

    // 监听 value 变化
    watch(
        () => props.value,
        (newValue, oldValue) => {
          if (newValue) {
            parseMarkdown();
          }
        },
        {immediate: true}
    );

    onMounted(() => {
      if (props.value) {
        parseMarkdown();
      }
    });

    return {
      htmlContent,
    };
  },
});
</script>
<style >

</style>

4.引用markdown.vue

新建demo.vue


<template>
    <MarkDown :value="content"></MarkDown>
</template>

<script setup>
  import { ref, onMounted } from 'vue';
  import MarkDown from './MarkDown.vue'

  const content = ref('')

  // md文档导入
  const mdModules =  import.meta.glob('./提交示例.md', { as: 'raw' })
  onMounted(async () => {
      content.value = await mdModules['./提交示例.md']()
  })
</script>

5.效果图

更多推荐