可交互的Vue按钮
Vue3项目里怎么高效用好Markdown?新手和进阶问题都有答案 Markdown作为一种轻量级的标记语言,早就是技术博客、产品文档、在线教育内容的标配了,而Vue3现在又是前端开发的主流框架,把这俩结合起来做项目,既能快速生成排版工整的内容,又能保留Vue的响应式和组件化优势,不过很多刚上手的朋友可能会踩库选不对、样式不生效、代码高亮乱码这类坑,今天就把平时开发里遇到的高频问题,按从入门到进阶的顺序整理出来,看完就能直接落地。
新手入门第一步:Vue3项目里主流的Markdown渲染库有哪些?各有什么优劣势?
首先得明确,直接写.md文件Vue是不会自动解析的,必须引入第三方库,目前项目里用得最多的有三个,分别是marked+DOMPurify组合、markdown-it系列,还有专门给Vue3做的组件库@vueuse/markdown。
先说说marked+DOMPurify,这俩是最基础的组合,体积小得离谱,压缩后加起来也就几十KB。marked负责把Markdown字符串转成HTML,速度超级快,对原生Markdown语法支持得最标准;DOMPurify则是用来做XSS防护的——毕竟把用户输入的Markdown直接转成HTML插入页面太危险了,别人写个<script>标签就能偷你的数据,新手如果只是做个简单的个人博客,展示自己写的静态Markdown,甚至可以先不加DOMPurify,但如果涉及用户提交内容,这步绝对不能省。
然后是markdown-it,这个库的扩展性比marked强太多了,官方和社区有几百个插件,比如支持数学公式的markdown-it-katex、支持流程图的markdown-it-mermaid、支持锚点跳转的markdown-it-anchor,基本上你能想到的Markdown增强功能,都能找到对应的插件,而且它的渲染速度也不算慢,适合做功能比较丰富的文档站或者在线写作平台,不过它的原生Markdown解析和marked有点小差异,比如表格的对齐方式语法,markdown-it更严格一点,刚开始用可能要适应一下。
@vueuse/markdown,这个是VueUse生态里的组件,本质上是封装了marked+DOMPurify(当然也可以换成markdown-it),还加了一些Vue3专属的功能,比如支持在Markdown里直接使用Vue组件、支持响应式更新Markdown内容,新手如果不想写太多配置代码,直接用这个组件会方便很多,比如引入之后直接写<VueUseMarkdown :source="content" />就能渲染了,不过它的扩展性相对弱一点,如果要加很多自定义插件,还是建议用纯markdown-it。
纯静态Markdown展示,新手怎么快速上手配置?
新手刚接触,肯定先做纯静态展示对吧?比如把项目根目录下docs文件夹里的.md文件,通过路由渲染到页面上,这里我推荐用VitePress会不会太麻烦?哦不对,是普通Vue3项目,不是文档站项目,还是用markdown-it系列吧,扩展性好,配置也不算复杂,而且后面想加功能随时能加。
在Vue3项目里安装依赖:如果用Vite创建的项目,直接终端输入npm install markdown-it markdown-it-anchor markdown-it-table-of-contents highlight.js就行——这里顺便加了代码高亮highlight.js、锚点跳转markdown-it-anchor、目录生成markdown-it-table-of-contents,这三个都是做技术类展示必备的。
创建一个工具函数文件,比如src/utils/markdown.js,用来配置和初始化markdown-it:
import MarkdownIt from 'markdown-it'
import mdAnchor from 'markdown-it-anchor'
import mdToc from 'markdown-it-table-of-contents'
import hljs from 'highlight.js/lib/core'
// 按需引入你常用的编程语言高亮,这样体积会小很多,比直接引入整个highlight.js小一半以上
import javascript from 'highlight.js/lib/languages/javascript'
import typescript from 'highlight.js/lib/languages/typescript'
import vue from 'highlight.js/lib/languages/vue'
import css from 'highlight.js/lib/languages/css'
import html from 'highlight.js/lib/languages/xml'
// 引入highlight.js的主题,这里选个常用的atom-one-dark,也可以换github-dark、monokai之类的
import 'highlight.js/styles/atom-one-dark.css'
// 注册高亮语言
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('typescript', typescript)
hljs.registerLanguage('vue', vue)
hljs.registerLanguage('css', css)
hljs.registerLanguage('html', html)
// 初始化markdown-it
const md = new MarkdownIt({
html: true, // 允许Markdown里直接写HTML标签,不过要注意配合DOMPurify使用
linkify: true, // 自动把URL转成链接
typographer: true, // 美化标点符号,比如把--转成破折号
highlight: function (str, lang) {
// 代码高亮逻辑
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre><code class="hljs language-${lang}">${hljs.highlight(str, { language: lang }).value}</code></pre>`
} catch (__) {}
}
// 如果没有指定语言或者不支持,就用默认的高亮
return `<pre><code class="hljs">${md.utils.escapeHtml(str)}</code></pre>`
}
})
// 注册插件
md.use(mdAnchor, {
permalink: mdAnchor.permalink.headerLink() // 给标题加个点击复制锚点的小图标,体验更好
})
md.use(mdToc, {
includeLevel: [1, 2, 3], // 只生成h1到h3的目录
containerClass: 'markdown-toc', // 给目录加个自定义类名,方便写样式
markerPattern: /^\[toc\]/im // 只要在Markdown里写[toc]就会生成目录
})
export default md
创建一个Markdown渲染组件,比如src/components/MarkdownRenderer.vue:
<template>
<div class="markdown-content" v-html="renderedContent"></div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import md from '@/utils/markdown'
import DOMPurify from 'dompurify' // 别忘了安装DOMPurify:npm install dompurify
import { useRoute } from 'vue-router' // 如果是通过路由加载Markdown文件,需要用useRoute
const props = defineProps({
source: {
type: String,
default: ''
},
filePath: {
type: String,
default: ''
}
})
const renderedContent = computed(() => {
// 先转成HTML,再用DOMPurify过滤
return DOMPurify.sanitize(md.render(props.source))
})
const route = useRoute()
// 如果是通过路由加载Markdown文件,比如路由是/markdown/:file,就可以在这里获取file并加载
onMounted(async () => {
if (props.filePath) {
// 用Vite的动态导入语法加载.md文件,注意Vite默认不支持直接导入.md,需要加个插件吗?
// 哦对了,Vite 4.2+默认支持静态资源的导入,但.md文件会被当成字符串处理,不过可能需要在vite.config.js里加个配置?
// 其实不用,直接用import.meta.glob或者fetch就行,用fetch更灵活
try {
const response = await fetch(props.filePath)
const text = await response.text()
// 这里要注意,组件的props不能直接修改,所以我们可以用ref来存filePath对应的内容
// 不过刚才的renderedContent是computed,依赖props.source,那我们可以把source作为ref,然后当filePath变化时更新source
// 对哦,刚才的props和computed写得有点问题,重新改一下
// (这里先不贴完整的修正后的代码,后面会说踩坑的时候提到)
} catch (error) {
console.error('加载Markdown文件失败:', error)
}
}
})
</script>
<style scoped>
/* 这里给Markdown内容加一些基础样式,因为默认的HTML标签样式太丑了 */
.markdown-content {
line-height: 1.8;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
padding: 20px;
}
.markdown-content h1 {
font-size: 2.5rem;
margin: 2rem 0 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #eee;
}
.markdown-content h2 {
font-size: 2rem;
margin: 1.8rem 0 0.9rem;
}
.markdown-content h3 {
font-size: 1.5rem;
margin: 1.5rem 0 0.7rem;
}
.markdown-content p {
margin: 1rem 0;
}
.markdown-content a {
color: #007bff;
text-decoration: none;
}
.markdown-content a:hover {
text-decoration: underline;
}
.markdown-content pre {
background-color: #282c34;
padding: 1.2rem;
border-radius: 8px;
overflow-x: auto;
margin: 1.5rem 0;
}
.markdown-content code {
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 0.9rem;
}
/* 目录的样式 */
.markdown-toc {
background-color: #f8f9fa;
padding: 1.5rem;
border-radius: 8px;
margin: 1.5rem 0;
}
.markdown-toc ul {
list-style: none;
padding-left: 0;
}
.markdown-toc ul ul {
padding-left: 1.5rem;
margin-top: 0.5rem;
}
.markdown-toc li {
margin: 0.5rem 0;
}
</style>
新手最容易踩的三个坑是什么?怎么解决?
刚才的代码里其实留了一个小伏笔,就是加载Markdown文件的问题,这也是新手最容易踩的第一个坑:Vite默认怎么加载静态Markdown文件?
很多新手会直接用import('./docs/test.md'),结果发现报错,说找不到模块或者模块类型不对,其实Vite 4.2+确实默认不支持把.md文件当成ES模块导入,不过它支持把静态资源当成字符串导入,只要在导入路径后面加个?raw后缀就行,比如import testMd from './docs/test.md?raw'——这样直接就能拿到.md字符串,非常方便,刚才的组件里用fetch其实有点冗余,除非Markdown文件是放在服务器上的动态资源。
第二个坑是XSS防护被忽略了,刚才在工具函数里提了一句,但很多新手可能觉得“我的Markdown都是自己写的,不会有问题”,但万一以后项目扩展了,允许用户提交内容呢?或者你不小心复制了一段带恶意代码的Markdown呢?DOMPurify的配置非常简单,安装之后只需要在v-html之前加一行DOMPurify.sanitize()就行,千万不能省,而且要注意,markdown-it默认的html: true虽然方便,但也会增加XSS的风险,配合DOMPurify才能放心用。
第三个坑是自定义样式不生效,很多新手会在Markdown渲染组件的<style scoped>里写样式,结果发现有的样式生效,有的不生效,比如pre和code的样式生效了,但h1和h2的样式没生效,这是因为Vue的scoped样式只会给组件内部的元素加一个唯一的属性选择器,比如data-v-xxxxx,而通过v-html插入的HTML标签是不会加这个属性的,所以scoped样式对它们无效,解决方法有两个:第一个是把自定义样式放在全局CSS里,比如src/assets/css/global.css,然后在main.js里引入;第二个是用deep()伪类(Vue3里的写法,Vue2里是:v-deep或者/deep/),比如把pre的样式写成deep(.markdown-content pre)——不过用全局CSS更统一,特别是如果项目里有多个地方用到Markdown渲染组件的话。
进阶玩法一:怎么在Markdown里直接使用Vue组件?
如果只是展示纯静态Markdown,可能满足不了一些进阶需求,比如你想在技术文档里加一个可交互的Vue按钮,或者展示一个自己写的Vue组件的效果,这时候就可以用@vueuse/markdown组件,或者用vite-plugin-markdown插件(这个插件不仅能加载Markdown文件,还能把Markdown里的Vue组件解析出来)。
这里推荐用vite-plugin-markdown,因为它的配置更灵活,而且可以和markdown-it配合使用,保留markdown-it的所有插件功能,安装依赖:npm install vite-plugin-markdown @vueuse/markdown——哦不对,vite-plugin-markdown本身就支持解析Vue组件,不需要@vueuse/markdown,刚才说重复了。
在vite.config.js里配置插件:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Markdown from 'vite-plugin-markdown'
export default defineConfig({
plugins: [
vue(),
Markdown({
mode: ['vue'], // 只生成Vue组件模式的输出
markdownItOptions: {
// 这里可以把之前在utils/markdown.js里的markdown-it配置搬过来
html: true,
linkify: true,
typographer: true
},
// 这里可以注册markdown-it插件
markdownItSetup(md) {
// 刚才的mdAnchor、mdToc、highlight.js都可以在这里注册
}
})
]
})
在Markdown里直接用Vue组件就行,
这是一个用Vue3写的计数器组件:
<Counter />
```vue
<template>
<button @click="count++">点击次数:{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
不过要注意,`vite-plugin-markdown`默认只会解析全局注册的Vue组件,如果你想解析局部组件,需要在Markdown文件的开头加一个`frontmatter`(YAML格式的前置元数据),
```markdown
---
components:
Counter: '@/components/Counter.vue'
---
<Counter />
创建一个Markdown页面组件,比如src/views/MarkdownPage.vue:
<template>
<div class="markdown-page">
<!-- 因为vite-plugin-markdown把.md文件转成了Vue组件,所以直接引入使用就行 -->
<TestMd />
</div>
</template>
<script setup>
// 这里用动态导入或者静态导入都行,静态导入的话路径后面要加?vue后缀
import TestMd from '@/docs/test.md?vue'
</script>
进阶玩法二:怎么支持数学公式和流程图?
刚才的markdown-it扩展性强的优势就体现出来了,支持数学公式和流程图只需要安装对应的插件就行。
先说说数学公式,推荐用markdown-it-katex,因为它的渲染速度比MathJax快很多,而且体积也小,安装依赖:npm install markdown-it-katex katex——别忘了安装katex本体,因为markdown-it-katex只是个适配器,在markdown-it的配置里注册插件:
import mdKatex from 'markdown-it-katex'
import 'katex/dist/katex.min.css' // 引入katex的主题
md.use(mdKatex, {
throwOnError: false // 出错的时候不要抛出异常,而是显示错误信息
})
在Markdown里用数学公式就行,行内公式用包裹,块级公式用包裹,
# 数学公式示例
行内公式:爱因斯坦的质能方程是 $E = mc^2$。
块级公式:
$$
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
$$
再说说流程图,推荐用markdown-it-mermaid,因为Mermaid的语法非常简单,而且支持很多种图表,比如流程图、时序图、类图、甘特图等等,安装依赖:npm install markdown-it-mermaid mermaid——同样要安装mermaid本体,在markdown-it的配置里注册插件:
import mdMermaid from 'markdown-it-mermaid'
import mermaid from 'mermaid'
// 初始化mermaid
mermaid.initialize({
startOnLoad: false // 不要自动加载,因为通过markdown-it-mermaid渲染的话,会自动调用渲染函数
})
md.use(mdMermaid, {
mermaid: mermaid
})
在Markdown里用Mermaid语法写流程图就行,
# 流程图示例
```mermaid
flowchart LR
A[开始] --> B{是否是Vue3项目?}
B -->|是| C[安装markdown-it系列库]
B -->|否| D[升级到Vue3]
C --> E[配置markdown-it和插件]
D --> E
E --> F[创建Markdown渲染组件]
F --> G[完成!]
##
今天从库的选择、纯静态展示配置、新手避坑、进阶玩法四个方面,讲了Vue3项目里怎么高效用好Markdown,纯静态展示选`marked`+`DOMPurify`,功能丰富选`markdown-it`系列,不想写太多配置选`@vueuse/markdown`;新手一定要注意Vite加载Markdown文件加`?raw`后缀、XSS防护不能省、自定义样式不用scoped或者用`:deep()`;进阶玩法可以试试`vite-plugin-markdown`解析Vue组件、`markdown-it-katex`支持数学公式、`markdown-it-mermaid`支持流程图。
Markdown和Vue3的结合还有很多其他玩法,比如支持Markdown内容的实时编辑预览、支持导出PDF、支持多语言Markdown等等,感兴趣的朋友可以自己去探索一下,如果在开发过程中遇到什么问题,也可以在评论区留言,大家一起讨论解决。 版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



