学习
最近本来想用 vuepress 做一个公用组件说明页,然后想了解一下 md 是如何变成 html 且能够自由切换主题,最终想自己实现一个类似的功能。
效果图
前置知识
gray-matter:库用来对 md 文件进行解析
md 文件内容
---
title: Hello
slug: home
---
# Hello world!
```js
function greet(name) {
console.log("Hello, " + name + "!");
}
```
解析后
```json
{
content: '\n' +
'# Hello world!\n' +
'\n' +
'```js\n' +
'function greet(name) {\n' +
' console.log("Hello, " + name + "!");\n' +
'}\n' +
'```\n',
data: { title: 'Hello', slug: 'home' },
isEmpty: false,
excerpt: ''
}
marked:将解析后的content转换成HTML内容
prismjs:代码高亮
安装
npm i prims
使用
//css
@import url("./style/themes/prism-coy.css");
@import url("./style/themes/prism-tomorrow.css");
//js
import Prism from "prismjs";
Prism.highlightAll();
因为要使用主题,所以我将prims的样式复制了一份到本地.使用node-sass将scss转成css
将prism.css改成prism.scss,加上自己想要的主题名称,再进行转换
npx node-sass ./prism.scss ./prism.css
主题切换
根据frontMatter格式获取对应的theme主题
---
theme: coy
---
代码链接
具体代码
后端
const matter = require("gray-matter");
const fs = require("fs");
const { marked } = require("marked");
const data = fs.readFileSync("./test.md", "utf-8");
// 其他部分省略可拉代码瞅瞅
router.post("/mdtohtml", async (ctx) => {
const { content, data: forntMatter } = matter(data);
const html = marked.parse(content);
ctx.body = {
html,
forntMatter,
};
});
前端
<template>
<div id="app" >
<button @click="changeTheme('coy')">coy主题</button>
<button @click="changeTheme('tomorrow')">tomorrow主题</button>
<div class="markdown-box">
<div class="mark-code" contenteditable="true" @input="setContent"></div>
<div :class="`theme-${theme} mark-content`">
<div id="md-container"></div>
</div>
</div>
</div>
</template>
<script>
import Prism from "prismjs";
import axios from "axios";
export default {
name: "App",
data() {
return {
theme: "coy",
value:"123"
};
},
mounted() {
// this.init();
},
methods: {
init(text) {
axios.post("http://localhost:3003/mdtohtml",{text:text},).then((res) => {
let {html,forntMatter} = res.data;
let app = document.getElementById("md-container");
//在说有pre前加上复制按钮
html = html.replace(/<pre>/g, '<div id="copy" class="copy-box"><span class="copy">复制<span></div><pre>');
app.innerHTML = html;
console.log(html);
this.theme = forntMatter.theme
Prism.highlightAll();
//绑定复制按钮click
this.bindCopy()
});
},
bindCopy(){
let copys = document.querySelectorAll('.copy')
//遍历所有code添加事件
copys.forEach(item=>{
item.addEventListener('click',function(){
const codeBlock =item.parentElement.nextElementSibling.children[0]
const codeContent = codeBlock.innerText;
navigator.clipboard.writeText(codeContent)
.then(() => {
console.log('复制成功',codeContent)
})
.catch((error) => {
console.error('复制失败:', error);
});
})
})
},
setContent(e){
console.log(e.target.innerText)
let text = e.target.innerText
this.init(text)
},
changeTheme(theme) {
this.theme = theme;
},
},
};
</script>
<style lang="scss">
@import url("./style/themes/prism-coy.css");
@import url("./style/themes/prism-tomorrow.css");
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
.markdown-box{
display: flex;
height: calc(100vh - 30px);
}
@mixin mark-block {
width: 50%;
height: 100%;
border: solid 1px #606266;
padding: 10px
}
.mark-code{
@include mark-block;
background: #eee;
&:focus{
outline: none;
}
line-height: 26px;
}
.mark-content{
@include mark-block;
border-left: 0;
}
</style>
- changeTheme:对 class 进行了切换
- bindCopy:获取复制按钮并添加复制功能
完结
完结撒花,点个赞吧~