概述
在CSDN文章和AI问答的产生的回答,都是markdown格式的文本,如果浏览器需要渲染对应的markdown文件,需要能够正确将markdown文件转化HTML字符串,这样才能被浏览器识别,比较常见的转化markdown文件的库有诸如:markedjs、markdown-it等,这些库能够将markdown转化为浏览器能够识别的标签内容,但是里面的代码块样式需要我们自定义处理,可以结合highlight.js进行高亮和主题定制。如果需要在markdown文件转化过程中,自定义部分响应内容,需要使用到对应的扩展。
效果
实现类似CSDN的代码块复制功能,如下在原markdown的文本基础上,扩展自定义的一部分内容,比如点击下面复制,可以复制对应代码块的内容

实现
需要使用到的库
- marked:转化markdown文本
- marked-highlight:marked的扩展,用于定制高亮样式
- highlight.js:高亮代码块
具体实现
这里结合vue代码来实现,代码块上面的头部,我们定义成组件,通过组件挂载的形式注入到最后生成的markdown渲染的html片段中去。 App.vue
js
<script setup lang="ts">
import { Marked, marked } from "marked";
import { markedHighlight } from "marked-highlight";
import { ref, createApp, nextTick, h } from "vue";
import HeaderCode from "./components/HeaderCode.vue";
import { str, codeStr } from "./test.js";
import { v1 } from "uuid";
import hljs from "highlight.js";
import "highlight.js/styles/github-dark.min.css";
window.myNamespace.age = 12;
const markdownContent = ref(null);
function handleTest() {
//高亮主题配置
const marked = new Marked(
markedHighlight({
emptyLangClass: "hljs",
langPrefix: "hljs language-",
highlight(code, lang, info) {
console.log("lang", lang, info);
const language = hljs.getLanguage(lang) ? lang : "plaintext";
return hljs.highlight(code, { language }).value;
},
})
);
//自定义代码块的markdown渲染逻辑
const renderer = {
code(data) {
const app = createApp({
render() {
return h(HeaderCode,{data});
},
});
//拦截code返回的内容,然后自定义注入头部组件
const parentNodeId = "code-header-" + v1();
nextTick(() => {
const container = document.querySelector(`#${parentNodeId}`);
console.log("container", container);
app.mount(container);
});
//返回渲染的内容
return `
<div id="${parentNodeId}" class="code-header">
</div>
<pre><code class="hljs language-js">${data.text}</code></pre>
`;
},
};
marked.use({ renderer });
const parseStr = marked.parse(str);
markdownContent.value.innerHTML = parseStr;
}
</script>
<template>
<el-button @click="handleTest">模拟</el-button>
<div class="markdown-content" ref="markdownContent"></div>
</template>
<style>
.code-header {
justify-content: space-between;
align-items: center;
width: 100%;
padding: 6px 14px 6px 6px;
display: flex;
background-color: #f5f5f5;
}
.copy-code {
cursor: pointer;
}
#app {
height: 100%;
padding: 10px;
}
body {
width: 100%;
height: 100%;
display: block;
}
</style>
HeaderCode.vue
js
<script setup lang="ts">
import { ElMessage } from "element-plus";
const props = defineProps<{ data: any }>();
const downLoad = () => {
console.log("downLoad", props.data);
const content = props.data.raw.replace(/```/g, "");
console.log("content", content);
const reg = new RegExp(props.data.lang);
console.log("res--code", content.replace(reg, ""));
ElMessage.success("复制成功");
};
</script>
<template>
<div class="code-title">{{ props.data.lang }}</div>
<div class="copy-code" @click="downLoad">复制</div>
</template>
总结
上面只拦截了code块的自定义内容,其他的,比如想要自定义标题、列表、图片等等的渲染逻辑,可以通过对应扩展进行处理。