Code前端首页关于Code前端联系我们

Vue3 Markdown渲染的完整踩坑指南

terry 5天前 阅读数 1088 #Vue

Vue3项目里怎么选Markdown渲染库?完整踩坑指南来帮你

做技术博客、协作文档、甚至带富文本但不想自己写复杂编辑器的Vue3小工具,选对Markdown渲染库是第一步,最近半年帮团队和身边朋友改了不下5个Vue3项目的Markdown相关功能,踩过的坑能凑成半页开发笔记,今天就把这些踩坑经验、对比思路和具体落地全理清楚,你看完至少能省3天的试错时间。

为什么不能直接用原生JS的Markdown库?

很多刚接触Vue3的开发者会直接把marked、remark这类原生库的CDN链接丢到index.html里,或者直接npm install后在组件里import用,第一次跑可能觉得没问题,等遇到复杂需求就傻眼了。

原生库的核心功能确实是解析Markdown文本成HTML字符串,但Vue3项目有它特殊的需求场景: 第一,安全防护:原生库默认不处理XSS漏洞,要是你的Markdown内容来自用户上传——比如评论区、协作文档,别人写个<script>alert('你好呀偷数据')</script>或者更隐蔽的代码,直接渲染HTML字符串会让浏览器直接执行,虽然可以自己加DOMPurify,但整合起来还要兼顾性能和灵活性,新手容易搞砸。 第二,组件替换:Vue3的核心优势是组件化,你总不能用原生库解析完后,再用innerHTML渲染,再通过DOM操作把![](image.jpg)改成带懒加载、预览弹窗的Vue3图片组件吧?这样不仅代码耦合,还会丢失Vue的响应式特性。 第三,语法扩展:现在大家用Markdown都不止用基础语法了,代码高亮、表格样式、数学公式(KaTeX/MathJax)、流程图(Mermaid)这些几乎是标配,原生库虽然有插件系统,但和Vue3的响应式、插槽机制适配得不好,配置起来非常繁琐。 第四,服务端渲染(SSR)支持:如果你的项目是Nuxt3或者VitePress这类SSR/SSG框架,原生库可能会因为依赖浏览器API(比如marked-highlight依赖window来获取Prism)而在构建或运行时报错,还要额外写判断逻辑,增加维护成本。

Vue3主流Markdown渲染库有哪些?分别适合什么场景?

现在市面上专门适配Vue3的Markdown渲染库其实不多,能稳定用的主要有3个:markdown-it-vue3、v-md-editor、@vueuse/markdown,先别着急选,先看每个库的定位、优缺点和适配场景,踩坑的第一步就是“别错配”。

定位1:轻量展示为主——选markdown-it-vue3

如果你只是想在项目里渲染用户写的或者从CMS拿过来的Markdown文章,不需要实时编辑、不需要复杂的扩展(或者只需要代码高亮、数学公式这类固定扩展),那markdown-it-vue3绝对是首选。

这个库是基于原生的markdown-it和markdown-it-container重构的,专门做Vue3的展示层渲染,体积非常小——核心库+基础插件压缩后大概只有120KB左右,加载速度很快,核心优势有两个: 第一个是组件替换超级简单,它不是用innerHTML渲染解析后的HTML,而是把Markdown的每个节点转换成Vue的虚拟DOM(VNode),然后通过插槽或者全局/局部组件注册的方式替换默认节点,比如你想把所有的标题改成带锚点跳转、点击复制链接的自定义组件,只需要在组件里写<template #h2="{ attrs, children }">...</template>就行,完全符合Vue3的开发习惯,响应式也能正常传递。 第二个是XSS防护内置可选,可以通过配置直接启用DOMPurify,不用自己手动整合,而且DOMPurify的配置项也能透传,灵活性足够,比如你可以允许用户插入<iframe>标签来嵌入B站视频,只需要在DOMPurify的配置里加ALLOWED_TAGS: ['iframe', ...]就行。

不过它也有缺点:第一,没有自带的编辑器,要是需要用户输入Markdown,你得自己找一个输入框搭配;第二,插件系统还是markdown-it的插件,虽然大部分插件能用,但有些插件因为是基于DOM操作的,可能需要做一些调整才能适配虚拟DOM的渲染方式;第三,没有官方维护的Mermaid、KaTeX之类的高级扩展,需要自己结合markdown-it的插件和Vue3的组件来写,比如Mermaid,你可以先注册一个自定义的代码块容器,然后在替换代码块的组件里用Mermaid的API渲染,还要注意SSR兼容。

这个库最适合的场景是:技术博客(展示文章)、产品文档(静态展示+少量自定义组件)、带用户评价但评价只支持基础Markdown的电商项目。

定位2:实时编辑+展示全搞定——选v-md-editor

如果你需要的是一个完整的Markdown编辑器,既有左侧编辑区、右侧预览区,又有工具栏(加粗、斜体、插入图片、插入链接这些一键操作),还支持所有主流的扩展,那v-md-editor是目前Vue3生态里最好的选择,没有之一。

这个库是专门为Vue3打造的,定位就是“开箱即用的Markdown编辑器”,分为轻量版、标准版、进阶版三个版本,你可以根据自己的需求选择:

  • 轻量版:体积最小(压缩后大概200KB左右),只有基础的编辑和预览功能,适合不需要复杂扩展的小工具;
  • 进阶版:在轻量版的基础上增加了图片上传(支持本地、阿里云OSS、七牛云、腾讯云COS,甚至可以自定义上传接口)、图片拖拽上传、代码块一键复制、TOC目录生成、全屏编辑、同步滚动这些常用功能;
  • 还有一个VuePress主题版,专门给VuePress 2.x用的,你可以直接用它替换VuePress的默认编辑器,写文档更方便。

核心优势除了全功能之外,还有文档非常详细,中文友好——这对国内开发者来说太重要了,你遇到的99%的问题都能在官方文档里找到答案,比如怎么自定义上传接口、怎么配置图片上传的大小限制、怎么配置代码高亮的主题(支持Prism和Highlight.js两种高亮库,主题也有很多种可选)。

v-md-editor的扩展性也非常强:第一,支持自定义工具栏按钮,你可以添加自己的工具栏按钮,比如添加一个插入B站视频的按钮;第二,支持自定义Markdown语法扩展,比如你想添加一个自定义的警告框语法:: warning 这是一个警告:::,只需要注册一个自定义的插件就行;第三,组件替换也支持,和markdown-it-vue3类似,你可以通过插槽替换默认的组件,比如替换默认的代码块组件、图片组件等等。

当然它也有缺点:第一,体积比较大,尤其是标准版和进阶版,压缩后大概有500KB左右,如果你的项目对体积要求非常高,可能不太适合;第二,图片上传功能默认会把图片上传到服务器,要是你的项目不需要图片上传,或者只需要插入本地图片的base64编码,你可以通过配置禁用图片上传功能,只保留插入本地图片的base64编码功能;第三,没有官方维护的数学公式扩展,需要自己结合KaTeX或者MathJax来写扩展。

这个库最适合的场景是:协作文档(实时编辑+展示)、在线笔记应用(实时编辑+展示+保存)、技术博客的后台编辑页面(编辑文章)。

定位3:极简主义,只需要渲染Markdown文本——选@vueuse/markdown

如果你是@vueuse的粉丝,或者你的项目已经用了@vueuse,那@vueuse/markdown是一个不错的选择,它是@vueuse的一个子模块,专门用来渲染Markdown文本,体积超级小——核心库压缩后大概只有30KB左右,加载速度非常快。

这个库的核心优势就是极简主义,没有多余的功能,只有一个useMarkdown的Composable,你只需要传入Markdown文本,它就会返回解析后的HTML字符串,然后你可以用Vue的v-html指令渲染,或者把HTML字符串转换成VNode再渲染,它也支持markdown-it的插件,你可以通过配置添加自己的markdown-it插件,比如添加代码高亮插件、数学公式插件等等。

不过它的缺点也很明显:第一,没有自带的XSS防护,你需要自己手动整合DOMPurify;第二,没有组件替换功能,只能用v-html指令渲染解析后的HTML字符串,或者自己把HTML字符串转换成VNode再渲染,非常麻烦;第三,没有编辑器功能,只能展示Markdown文本;第四,文档比较简单,只有英文文档,对国内开发者不太友好。

这个库最适合的场景是:极简主义的小工具(只需要渲染Markdown文本)、已经用了@vueuse的项目(不想再引入额外的库)。

具体怎么落地?以markdown-it-vue3展示技术博客文章为例

很多开发者可能更关心具体怎么落地,那我就以markdown-it-vue3展示技术博客文章为例,给大家写一个完整的例子,包括怎么安装依赖、怎么配置插件、怎么组件替换、怎么启用XSS防护。

第一步:安装依赖

首先需要安装核心库markdown-it-vue3,然后根据自己的需求安装插件,比如代码高亮插件highlight.js、代码高亮的Vue3适配插件markdown-it-highlightjs、数学公式插件KaTeX、数学公式的Vue3适配插件markdown-it-katex、DOMPurify插件(用于XSS防护):

npm install markdown-it-vue3 highlight.js markdown-it-highlightjs katex markdown-it-katex dompurify

或者用yarn:

yarn add markdown-it-vue3 highlight.js markdown-it-highlightjs katex markdown-it-katex dompurify

第二步:全局注册markdown-it-vue3组件

在main.js或者main.ts里全局注册markdown-it-vue3组件,这样所有的组件都可以直接用:

import { createApp } from 'vue'
import App from './App.vue'
import MarkdownItVue3 from 'markdown-it-vue3'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css' // 导入highlight.js的github主题
import markdownItHighlightjs from 'markdown-it-highlightjs'
import katex from 'markdown-it-katex'
import 'katex/dist/katex.min.css' // 导入KaTeX的默认主题
import DOMPurify from 'dompurify'
const app = createApp(App)
// 配置markdown-it-vue3
app.use(MarkdownItVue3, {
  // 配置markdown-it的插件
  plugins: [
    markdownItHighlightjs({ hljs }), // 配置代码高亮插件
    katex // 配置数学公式插件
  ],
  // 配置DOMPurify
  doPurify: true, // 启用XSS防护
  purifyOptions: {
    // 这里可以透传DOMPurify的配置项
    ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img', 'code', 'pre', 'blockquote', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'br', 'hr', 'strong', 'em', 'i', 'b', 'del', 'ins', 'sub', 'sup', 'div', 'span', 'iframe'], // 允许的标签
    ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'id', 'width', 'height', 'frameborder', 'allowfullscreen'] // 允许的属性
  }
})
app.mount('#app')

第三步:自定义组件替换

接下来在组件里自定义组件替换,比如替换默认的代码块组件(加上一键复制功能)、图片组件(加上懒加载功能)、标题组件(加上锚点跳转功能):

<template>
  <div class="blog-post">
    <!-- 这里的content是从CMS拿过来的Markdown文章内容 -->
    <markdown-it-vue3 :content="content">
      <!-- 替换默认的代码块组件 -->
      <template #pre="{ attrs, children }">
        <div class="code-block-wrapper">
          <pre v-bind="attrs">{{ children }}</pre>
          <button class="copy-btn" @click="copyCode(children)">复制代码</button>
        </div>
      </template>
      <!-- 替换默认的图片组件 -->
      <template #img="{ attrs }">
        <img v-bind="attrs" loading="lazy" class="blog-img" />
      </template>
      <!-- 替换默认的h2标题组件 -->
      <template #h2="{ attrs, children }">
        <h2 v-bind="attrs" :id="generateId(children)">
          {{ children }}
          <a :href="'#' + generateId(children)" class="anchor-link">#</a>
        </h2>
      </template>
      <!-- 替换默认的h3标题组件 -->
      <template #h3="{ attrs, children }">
        <h3 v-bind="attrs" :id="generateId(children)">
          {{ children }}
          <a :href="'#' + generateId(children)" class="anchor-link">#</a>
        </h3>
      </template>
    </markdown-it-vue3>
  </div>
</template>
<script setup>
import { ref } from 'vue'
// 从CMS拿过来的Markdown文章内容
const content = ref(`
## 为什么不能直接用原生JS的Markdown库?
很多刚接触Vue3的开发者会直接把marked、remark这类原生库的CDN链接丢到index.html里...
## 轻量展示为主——选markdown-it-vue3
这个库是基于原生的markdown-it和markdown-it-container重构的...
### 核心优势1:组件替换超级简单
```javascript
const app = createApp(App)
app.use(MarkdownItVue3)

`) 的id(用于锚点跳转) const generateId = (children) => { // 这里的children是一个VNode数组,需要把它转换成字符串 return children.map(child => child.children?.toString() || '').join('').replace(/\s+/g, '-').toLowerCase() }

// 复制代码 const copyCode = async (children) => { // 这里的children是一个VNode数组,需要把它转换成字符串 const code = children.map(child => child.children?.toString() || '').join('') try { await navigator.clipboard.writeText(code) alert('复制成功!') } catch (err) { // 如果不支持navigator.clipboard,就用传统的方法 const textarea = document.createElement('textarea') textarea.value = code document.body.appendChild(textarea) textarea.select() document.execCommand('copy') document.body.removeChild(textarea) alert('复制成功!') } }

```

第四步:测试一下

现在你可以运行项目,看看效果如何,比如Markdown文章内容有没有渲染成功、代码块有没有高亮、数学公式有没有渲染成功、XSS防护有没有生效(可以在Markdown文章内容里写一个<script>alert('你好呀偷数据')</script>试试,看看会不会执行)。

踩过的那些坑,你一定要避开

刚才说了那么多,现在再给大家总结一下最近半年踩过的那些坑,你一定要避开:

坑1:XSS防护没做好

这个是最严重的坑,要是你的Markdown内容来自用户上传,XSS防护没做好,会导致用户的数据被盗,甚至网站被黑,所以不管你用哪个库,一定要启用XSS防护,或者自己手动整合DOMPurify。

坑2:组件替换时丢失响应式特性

要是你用markdown-it-vue3或者v-md-editor的组件替换功能,一定要注意不要丢失Vue的响应式特性,比如替换图片组件时,要是图片的src是响应式的,一定要用v-bind绑定,不能直接用固定的src。

坑3:SSR不兼容

要是你的项目是Nuxt3或者VitePress这类SSR/SSG框架,一定要注意库的SSR兼容性,比如marked-highlight依赖window来获取Prism,会在构建或运行时报错,这时候你可以用Vite的ssr.noExternal配置,或者用Nuxt3的build.transpile配置,把marked-highlight排除在SSR之外。

坑4:体积太大

要是你的项目对体积要求非常高,一定要注意库的体积,比如v-md-editor的进阶版压缩后大概有500KB左右,要是你的项目不需要这么多功能,可以选轻量版,或者选markdown-it-vue3。

坑5:文档不详细

要是你选了一个文档不详细的库,遇到问题会非常麻烦,所以尽量选文档详细、中文友好的库,比如v-md-editor。

好了,今天的分享就到这里,

  • 要是你只是想轻量展示Markdown文章,不需要实时编辑,选markdown-it-vue3;
  • 要是你需要实时编辑+展示全搞定,选v-md-editor;
  • 要是你是@vueuse的粉丝,或者你的项目已经用了@vueuse,选@vueuse/markdown。

最后再提醒大家一句:选库之前一定要先看库的定位、优缺点和适配场景,别错配,不然踩坑的是你自己。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门