封装渲染函数,对传入的content解析,最后v-html插入即可。
import MarkdownIt from "markdown-it";
import texmath from "markdown-it-texmath";
import katex from "katex";
import hljs from "highlight.js";
import "katex/dist/katex.min.css";
// import "highlight.js/styles/github.css";
import "highlight.js/styles/atom-one-dark.css";
import DOMPurify from "dompurify";
// 初始化 markdown-it 实例(只初始化一次)
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value;
} catch (__) { }
}
return ""; // 使用默认的高亮
},
}).use(texmath, {
engine: katex,
delimiters: "dollars",
katexOptions: {
macros: {
"\\RR": "\\mathbb{R}",
"\\abs": ["\\left|#1\\right|", 1],
},
},
});
/**
* 渲染Markdown内容为HTML
* @param content Markdown格式的字符串
* @returns 渲染后的HTML字符串,可直接用于v-html
*/
export default function renderMarkdown(content) {
// content = `# 混合内容示例
// ## 数学与代码结合
// 计算圆的面积公式: $A = \\pi r^2$
// 实现代码:
// \`\`\`python
// import math
// def circle_area(radius):
// """计算圆的面积"""
// return math.pi * radius ** 2
// # 示例使用
// print(f"半径为5的圆面积: {circle_area(5):.2f}")
// \`\`\`
// ## 列表中的公式
// 1. 勾股定理: $a^2 + b^2 = c^2$
// 2. 二次方程求根公式:
// $$
// x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}
// $$
// 3. 欧拉公式: $e^{i\\pi} + 1 = 0$
// ## 表格示例
// | 函数名 | 描述 | 公式 |
// |--------|------|------|
// | 正弦函数 | 三角函数 | $\\sin(x)$ |
// | 指数函数 | 自然指数 | $e^x$ |
// | 对数函数 | 自然对数 | $\\ln(x)$ |
// `
return DOMPurify.sanitize(md.render(content));
}
css部分,可以根据渲染出来的html结构自己配css,下面是我用到的css代码,可以直接复制然后进行微调。
.text {
padding: 10px 20px;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 10px;
white-space: normal;
word-break: break-all;
border: 1px solid #e1e4e8;
color: #24292e;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
Arial, sans-serif;
font-size: 16px;
}
/* 标题样式 */
.text :deep(h1) {
font-size: 2em;
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.text :deep(h2) {
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
padding-bottom: 0.3em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.text :deep(h3) {
font-size: 1.25em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.text :deep(h4) {
font-size: 1em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.text :deep(h5) {
font-size: 0.875em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.text :deep(h6) {
font-size: 0.85em;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
color: #6a737d;
}
/* 段落和文字样式 */
.text :deep(p) {
margin-top: 0;
margin-bottom: 16px;
}
.text :deep(strong) {
font-weight: 600;
}
.text :deep(em) {
font-style: italic;
}
.text :deep(del) {
text-decoration: line-through;
}
/* 代码样式 */
.text :deep(code) {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
background-color: rgba(27, 31, 35, 0.05);
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 85%;
}
.text :deep(pre) {
background-color: #f6f8fa;
padding: 16px;
border-radius: 6px;
overflow: auto;
line-height: 1.45;
margin-bottom: 16px;
}
.text :deep(pre code) {
background-color: transparent;
padding: 0;
border-radius: 0;
font-size: 100%;
}
/* 引用块 */
.text :deep(blockquote) {
border-left: 4px solid #dfe2e5;
color: #6a737d;
padding: 0 1em;
margin: 0 0 16px 0;
}
.text :deep(blockquote > :first-child) {
margin-top: 0;
}
.text :deep(blockquote > :last-child) {
margin-bottom: 0;
}
/* 列表样式 */
.text :deep(ul),
.text :deep(ol) {
padding-left: 2em;
margin-top: 0;
margin-bottom: 16px;
}
.text :deep(li) {
margin-bottom: 0.25em;
}
.text :deep(li > p) {
margin-bottom: 0.25em;
}
.text :deep(li + li) {
margin-top: 0.25em;
}
.text :deep(ul ul),
.text :deep(ul ol),
.text :deep(ol ol),
.text :deep(ol ul) {
margin-top: 0;
margin-bottom: 0;
}
/* 表格样式 */
.text :deep(table) {
border-collapse: collapse;
width: 100%;
margin: 16px 0;
display: block;
overflow: auto;
}
.text :deep(table th) {
font-weight: 600;
background-color: #f6f8fa;
}
.text :deep(table th),
.text :deep(table td) {
border: 1px solid #dfe2e5;
padding: 6px 13px;
}
.text :deep(table tr) {
background-color: #fff;
border-top: 1px solid #c6cbd1;
}
.text :deep(table tr:nth-child(2n)) {
background-color: #f6f8fa;
}
/* 链接样式 */
.text :deep(a) {
color: #0366d6;
text-decoration: none;
}
.text :deep(a:hover) {
text-decoration: underline;
}
/* 分割线 */
.text :deep(hr) {
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: #e1e4e8;
border: 0;
}
/* 图片样式 */
.text :deep(img) {
max-width: 100%;
box-sizing: content-box;
background-color: #fff;
border-radius: 4px;
}
.text :deep(img[align="right"]) {
padding-left: 20px;
}
.text :deep(img[align="left"]) {
padding-right: 20px;
}
/* 任务列表 */
.text :deep(.task-list-item) {
list-style-type: none;
}
.text :deep(.task-list-item-checkbox) {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
/* Katex 公式样式 */
.text :deep(.katex) {
font-size: 1.1em;
}
/* 行内公式 */
.text :deep(.katex-display) {
margin: 1em 0;
overflow-x: auto;
overflow-y: hidden;
}
/* 代码块标题 */
.text :deep(.code-block-title) {
background: #f6f8fa;
border: 1px solid #e1e4e8;
border-bottom: 0;
padding: 6px 12px;
font-size: 85%;
font-weight: 600;
border-radius: 6px 6px 0 0;
margin-bottom: -16px;
}
/* 脚注 */
.text :deep(.footnotes) {
font-size: 85%;
color: #6a737d;
border-top: 1px solid #e1e4e8;
margin-top: 24px;
}
.text :deep(.footnotes li) {
margin-bottom: 0.5em;
}
.text :deep(.footnote-backref) {
margin-left: 0.5em;
text-decoration: none;
}