前端渲染markdown文件,文件内容可从后端获取或者从本地读取
本文介绍两种前端渲染md文件的方案:
- 后端把markdown文件发给前端,前端渲染在页面中
- 前端读取项目本地的md文件并渲染
先看效果图
md文件代码:
前端网页渲染:
先介绍node+express怎么提供接口:
const express = require("express"); const router = express.Router(); const fs = require("fs"); router.get("/api/getMarkDownContent", (req, res, next) => { //这里可以通过 query 拿到get请求的参数,可以实现根据参数返回不同的md文件等效果 const { query } = req; //拿到md文件的全路径 let filePath = process.cwd() + "/routes/test.md"; //利用node自带的fs模块------> 创建可读流 let fileStream = fs.createReadStream(filePath); //想要读取到可读流的数据,需要监听data事件 fileStream.on("data", function (data) { // res.write(data, "binary"); //直接返回 //封装返回 res.send({ code: 200, data: data.toString(), //Buffer转换为字符串 msg: '请求成功' }) }); //错误处理 fileStream.on("error", (err) => { next(err); }); //文件读取完毕 fileStream.on("end", function () { res.end(); }); }); module.exports = router;
再介绍前端axios获取数据以及渲染:
<template> <div class="markdown-preview"> <div v-html="renderedMarkdown"></div> </div> </template> <script setup> import hljs from 'highlight.js'; //需要 npm install highlight.js import 'highlight.js/styles/atom-one-dark.css'; // 选择一个自己喜欢的样式风格 import MarkdownIt from 'markdown-it'; //需要 npm install markdown-it import { getMdContentApi } from '@/xxx' //这里写你自己项目的http请求路径 import { ref, onMounted } from 'vue' let renderedMarkdown = ref('') onMounted(async () => { const md = new MarkdownIt({ html: true, // 允许在 Markdown 中使用 HTML linkify: true, // 自动转换链接为可点击的链接形式 typographer: true, // 启用智能引号等文本转换功能 highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { return `<pre class="hljs"><code>${hljs.highlight(lang, str, true).value}</code></pre>`; } catch (error) { console.log(error) } } return `<pre class="hljs"><code>${md.utils.escapeHtml(str)}</code></pre>`; }, }); const response = await getMdContentApi() renderedMarkdown.value = md.render(response.data); }) </script>
- 上面的例子用的是vue3。vue2或其它框架用法类似。
- 到这一步,页面已经把后端返回的markdown文件数据渲染成了html了。
- 部分样式需要自己去写才能达到满意的效果,我给我上面效果图中,自己加的一个样式代码:
// highlight.js .markdown-preview ul{ list-style-type: disc; } .markdown-preview ol{ list-style-type: decimal; } .markdown-preview .hljs{ font-size: 14px; padding: 14px 24px; overflow: auto; border-radius: 8px; } .markdown-preview li code,.markdown-preview p code{ background-color: #f0f0f0; border-radius: 3px; } .markdown-preview code{ line-height: 1.5; font-family: Menlo,Monaco,Consolas,'Courier New',monospace; } .markdown-preview h1,.markdown-preview h2,.markdown-preview h3,.markdown-preview h4,.markdown-preview h5,.markdown-preview h6 { line-height: 1.2; margin-top: 1em; margin-bottom: 16px; color: #000 } .markdown-preview h1 { font-size: 2.25em; font-weight: 300; padding-bottom: .3em } .markdown-preview h2 { font-size: 1.75em; font-weight: 400; padding-bottom: .3em } .markdown-preview h3 { font-size: 1.5em; font-weight: 500 } .markdown-preview h4 { font-size: 1.25em; font-weight: 600 } .markdown-preview h5 { font-size: 1.1em; font-weight: 600 } .markdown-preview h6 { font-size: 1em; font-weight: 600 } .markdown-preview h1,.markdown-preview h2,.markdown-preview h3,.markdown-preview h4,.markdown-preview h5 { font-weight: 600 } .markdown-preview h5 { font-size: 1em } .markdown-preview h6 { color: #5c5c5c } .markdown-preview strong { color: #000 } .markdown-preview del { color: #5c5c5c } .markdown-preview a:not([href]) { color: inherit; text-decoration: none } .markdown-preview a { color: #08c; text-decoration: none } .markdown-preview a:hover { color: #00a3f5; text-decoration: none } .markdown-preview img { max-width: 100% } .markdown-preview>p { margin-top: 0; margin-bottom: 16px; word-wrap: break-word } .markdown-preview>ol,.markdown-preview>ul { margin-bottom: 16px } .markdown-preview ol,.markdown-preview ul { padding-left: 2em } .markdown-preview ol.no-list,.markdown-preview ul.no-list { padding: 0; list-style-type: none } .markdown-preview ol ol,.markdown-preview ol ul,.markdown-preview ul ol,.markdown-preview ul ul { margin-top: 0; margin-bottom: 0 } .markdown-preview li { margin-bottom: 0 } .markdown-preview li.task-list-item { list-style: none } .markdown-preview li>p { margin-top: 0; margin-bottom: 0 } .markdown-preview .task-list-item-checkbox { margin: 0 .2em .25em -1.8em; vertical-align: middle } .markdown-preview .task-list-item-checkbox:hover { cursor: pointer } .markdown-preview blockquote { margin: 16px 0; font-size: inherit; padding: 0 15px; color: #5c5c5c; background-color: #f0f0f0; border-left: 4px solid #d6d6d6 } .markdown-preview blockquote>:first-child { margin-top: 0 } .markdown-preview blockquote>:last-child { margin-bottom: 0 } .markdown-preview hr { height: 4px; margin: 32px 0; background-color: #d6d6d6; border: 0 none } .markdown-preview table { margin: 10px 0 15px 0; border-collapse: collapse; border-spacing: 0; display: block; width: 100%; overflow: auto; word-break: normal; word-break: keep-all } .markdown-preview table th { font-weight: 700; color: #000 } .markdown-preview table td,.markdown-preview table th { border: 1px solid #d6d6d6; padding: 6px 13px } .markdown-preview dl { padding: 0 } .markdown-preview dl dt { padding: 0; margin-top: 16px; font-size: 1em; font-style: italic; font-weight: 700 } .markdown-preview dl dd { padding: 0 16px; margin-bottom: 16px } .markdown-preview blockquote,.markdown-preview dl,.markdown-preview ol,.markdown-preview p,.markdown-preview pre,.markdown-preview ul { margin-top: 0; margin-bottom: 16px } .markdown-preview kbd { color: #000; border: 1px solid #d6d6d6; border-bottom: 2px solid #c7c7c7; padding: 2px 4px; background-color: #f0f0f0; border-radius: 3px }
ok,大功告成 。
附上前端读取本地md文件并渲染方案:
https://github.com/unplugin/unplugin-vue-markdown
自律使我自由