markdonw转HTML(附带主题)

301 阅读1分钟

学习

最近本来想用 vuepress 做一个公用组件说明页,然后想了解一下 md 是如何变成 html 且能够自由切换主题,最终想自己实现一个类似的功能。

效果图

mdtohtmlcopy.gif

前置知识

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

image.png

将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:获取复制按钮并添加复制功能

完结

完结撒花,点个赞吧~