markdown渲染
日常文章写作中,markdown格式是一种常用的格式,在web端展示markdown内容,可以使用markdown.js来实现。
1. markdown.js集成
1.1 安装marked.js
bash
npm install marked
1.2 使用marked.js
把markdown内容渲染到页面中分为两步:
定义markdown内容 在setup中定义markdownContent ref,用于存储markdown内容。同时定义一个渲染方法renderedContent,用于讲markdown内容转换成html内容。
把html内容渲染到页面中 在template中定义一个div,id为markdown-container,v-html绑定渲染方法renderedContent。
具体代码如下:
vue
<template>
<div id="markdown-container" v-html="renderedContent"></div>
</template>
<script>
import { marked } from 'marked';
import { ref, onMounted, computed, watch, nextTick } from 'vue';
export default {
name: 'MarkdownRenderer',
setup() {
const markdownContent = ref(`# 标题1
## 标题2
### 标题3
\`\`\`python
def hello():
print('hello')
\`\`\`
<br>
## 标题4
`);
const renderedContent = computed(() => {
if (!markdownContent.value) {
return '暂无文档'
}
marked.setOptions({
renderer: new marked.Renderer(),
breaks: true,
highlight: function (code, lang) {
return prism.highlight(code, prism.languages[lang], lang);
}
})
return marked.parse(markdownContent.value);
});
return {
markdownContent,
renderedContent
}
},
data() {
return {
};
},
};
完成以上代码,就可以在页面中看到markdown内容了。但是我们发现H1,H2,H3等标题都没有加粗效果,这是因为markdown.js默认的渲染器没有对标题进行加粗处理。
2.增加madown样式
增加样式,我们使用github样式库github-markdown-css
.
2.1 安装github-markdown-css
bash
npm install github-markdown-css
2.2 使用github-markdown-css
- div标签增加样式id标签
class="markdown-body"
vue
<div id="markdown-container" v-html="renderedContent" class="markdown-body"></div>
style标签引入github-markdown-css
注意这一步,不要添加scoped,否则样式不会生效。
vue
<style>
@import 'github-markdown-css/github-markdown.css';
</style>
至此,markdown内容样式就可以在页面中正常显示了。但是我们发现代码块没有高亮效果,继续给代码块增加高亮效果。
3. 增加代码高亮效果
这里使用prismjs来实现代码高亮效果。也可以使用其他的代码高亮库,比如highlight.js。
3.1 安装prismjs
bash
npm install prismjs
由于对默认的主题样式不太满意,多安装一个prism-themes
库,来实现代码高亮效果。
bash
npm install prism-themes
3.2 使用prismjs
- 在script中引入prismjs头文件
vue
<script>
import { marked } from 'marked';
import { ref, computed } from 'vue';
import Prism from "prismjs";
import "prismjs/components/prism-cshtml";
import "prismjs/components/prism-css";
import "prismjs/components/prism-javascript";
import "prismjs/components/prism-python";
import "prismjs/components/prism-json";
import "prismjs/components/prism-jsx";
import "prismjs/components/prism-typescript";
</script>
在style中引入prism-themes主题样式atom-dark
注意:这一步不要添加scoped,否则样式不会生效。
vue
<style>
@import 'prism-themes/themes/prism-atom-dark.css'
</style>
到这一步,代码高亮效果已经实现了。
由于我们是markdown编辑器,在编辑的时候,发现代码块高亮丢失。在setup中增加以下代码触发编辑重渲染。
vue
setup() {
watch(renderedContent, () => {
nextTick(() => {
Prism.highlightAll();
});
});
}
4. 代码块增加复制按钮和行号
4.1 增加复制按钮和行号
vue
import "prismjs/plugins/line-numbers/prism-line-numbers.js";
import "prismjs/plugins/line-numbers/prism-line-numbers.css";
setup() {
const articleContent = ref(`# 标题1`);
const renderedContent = computed(() => {
if (!articleContent.value) {
return '暂无文档'
}
console.log('articleContent:', articleContent.value);
console.log('articleContent type:', typeof articleContent.value);
marked.setOptions({
breaks: true,
})
return marked.parse(articleContent.value);
});
const initPrism = () => {
nextTick(() => {
addLineNumbersClass();
Prism.highlightAll();
addCopyButtons();
});
};
watch(renderedContent, initPrism);
onMounted(initPrism);
const addLineNumbersClass = () => {
const codeBlocks = document.querySelectorAll('pre');
codeBlocks.forEach((block) => {
if (!block.classList.contains('line-numbers')) {
block.classList.add('line-numbers');
}
});
};
const addCopyButtons = () => {
const codeBlocks = document.querySelectorAll('pre');
codeBlocks.forEach((block) => {
if (!block.querySelector('.copy-button')) {
const button = document.createElement('button');
button.className = 'copy-button';
button.textContent = '复制';
button.addEventListener('click', () => copyCode(block));
block.appendChild(button);
}
});
};
const copyCode = (block) => {
const code = block.querySelector('code');
const range = document.createRange();
range.selectNode(code);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
try {
document.execCommand('copy');
alert('代码已复制到剪贴板');
} catch (err) {
console.error('复制失败:', err);
}
window.getSelection().removeAllRanges();
};
return {
articleContent,
renderedContent,
addCopyButtons,
addLineNumbersClass
}
},
4.2 行号和复制按钮样式
<style>
@import 'github-markdown-css/github-markdown.css';
@import 'prism-themes/themes/prism-atom-dark.css';
pre {
position: relative;
}
.copy-button {
position: absolute;
top: 5px;
right: 5px;
color: black;
padding: 5px 10px;
background-color: #f0f0f0;
border: none;
border-radius: 3px;
cursor: pointer;
}
.copy-button:hover {
background-color: #e0e0e0;
}
pre[class*="language-"] {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em;
letter-spacing: -1px;
border-right: 1px solid #999;
user-select: none;
}
.line-numbers-rows>span {
display: block;
counter-increment: linenumber;
}
.line-numbers-rows>span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}
</style>