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

Vue3项目里怎么正确集成并高效使用markdown-it?

terry 1周前 (08-14) 阅读数 997 #Vue
文章标签 Vue3it

很多开发Vue3个人博客、产品文档站或者带文本编辑展示需求的小工具时,都会碰到富文本编辑和渲染的问题,富文本编辑器可能太复杂、体积大,而直接展示markdown又方便编辑、易维护,这时候markdown-it就成了热门选择,不过不少人在集成时踩过坑:比如组件不会封装、代码高亮不生效、链接跳转有问题、怎么实现更高级的需求,甚至Vue3的单文件组件写法不兼容旧版本markdown-it插件?今天就一步步拆解这些问题,从最基础的安装配置到高阶插件定制,给你一套实用的方案。

先搞懂Vue3选markdown-it的核心原因

在正式开始集成前,先明确一个点:为什么是markdown-it,而不是marked或者其他库?这不是随大流,而是有实实在在的理由支撑。

轻量但扩展性强,核心库压缩后只有14KB左右,比大部分富文本编辑器轻很多,不会给首屏加载带来太大负担,更重要的是,它的插件生态特别完善——不管是加个代码高亮、生成目录、数学公式渲染,还是自定义表情、语法糖,甚至做反垃圾评论的转义过滤,都能找到成熟的现成插件,或者自己按照规则快速写一个。

支持CommonMark规范,还有GFM(GitHub Flavored Markdown)的默认适配计划?不对,默认核心库是完全遵循CommonMark 0.30的,但你可以通过插件一键开启GFM的全部特性,比如表格、任务列表、删除线这些日常用得最多的扩展,这个适配性对做开发者相关项目太友好了。

安全可控,默认情况下,markdown-it会对所有HTML标签进行严格转义,防止XSS攻击——这对有用户输入内容(比如博客评论区、社区问答帖)是最基础也最重要的安全保障,不需要你额外写太多转义逻辑,只需要根据需求调整安全选项就行。

Vue3项目中基础集成markdown-it的3种常见方式

现在开始讲集成,分三种场景:纯静态渲染、简单的组件封装复用、配合vue3-markdown-it这种第三方封装好的库快速上手,你可以根据自己项目的复杂度选。

(一)纯静态渲染:适合只展示一两个markdown内容的页面

如果你的项目需求特别简单,比如只是在首页放一段自己写的markdown自我介绍,或者在某个文档页展示一段固定的规则说明,那不需要封装组件,直接引入核心库,在mounted或者setup的onMounted钩子函数里处理就行。

举个例子,假设你用的是Vite创建的Vue3项目,具体步骤是:

  1. 先安装核心库:在终端里运行npm install markdown-it或者yarn add markdown-it都行。
  2. 在需要渲染的页面组件里,比如Home.vue,引入markdown-it的构造函数,还有一个注意点——如果你用的是<script setup>语法糖,要记得先实例化构造函数,不要在模板里直接用。
  3. 如果要渲染的markdown内容比较短,可以直接写在script里的变量里;如果比较长,推荐单独放在一个.md文件里,然后用Vite的静态资源导入语法(比如import rawMd from './assets/intro.md?raw')直接把内容读成字符串,这样维护起来更方便,不会和代码混在一起。
  4. 在模板里放一个容器,比如<div class="markdown-container"></div>,然后在onMounted里把实例化后的markdown-it处理好的HTML字符串赋值给这个容器的innerHTML。
  5. 最后别忘了给容器加样式!默认渲染出来的markdown没有任何CSS,就是光秃秃的文字,表格、代码块这些根本看不清,你可以自己写一套,也可以直接用成熟的markdown主题,比如GitHub的markdown-css,或者掘金的、简悦的都可以,直接在index.html或者组件的style标签里引入就行。

这里要提醒一个新手常犯的错误:不要直接在模板里用v-html绑定处理后的字符串吗?哦不对,直接用v-html其实也可以,但如果你的容器有复杂的结构或者后续要做动画,用innerHTML配合ref可能更灵活,但大部分纯静态场景v-html足够了,不过不管用哪种,都要注意安全问题——如果内容是用户输入的,一定要确保markdown-it的安全选项是开启的,默认是开启的,别随便改成false。

(二)简单的组件封装:适合多个页面需要渲染markdown的项目

如果项目里有好几个地方要渲染markdown,比如个人博客的文章列表页、详情页,那每次都要引入构造函数、处理内容、加ref或者v-html就太麻烦了,这时候封装一个通用的MarkdownRenderer.vue组件是最好的选择。

封装这个组件的时候,有几个关键点要注意:

  1. props的定义:至少要定义一个content prop,类型是String,用来接收要渲染的markdown字符串;还可以加一个options prop,类型是Object,用来动态调整markdown-it的配置,比如是否开启GFM扩展、是否允许部分HTML标签(要注意安全哦)、是否启用自动链接等。
  2. 组件的初始化:最好在组件的setup里,用computed或者watchEffect来实例化和处理内容——如果options是动态变化的,用watchEffect会更合适,每次options变了都会重新实例化构造函数并重新渲染;如果options是固定的,用computed就行,只在content变化的时候重新渲染,性能更好。
  3. 安全问题:一定要把options里的html默认值设为false,如果确实需要允许用户输入部分安全的HTML标签,比如<b><i><img>(但要限制img的src只能是本站或者信任的域名),不要直接改html为true,而是用markdown-it的html规则重写,或者用专门的安全过滤插件,比如markdown-it-sanitizer或者DOMPurify配合使用——DOMPurify更成熟,推荐这个组合。
  4. 样式问题:建议把样式写在组件的scoped样式标签里吗?不对,scoped样式只会作用于当前组件的根元素和直接子元素,而markdown渲染出来的HTML是动态生成的,有很多嵌套的子元素,scoped样式是生效不了的,所以要么在组件的style标签里去掉scoped,要么用deep()伪类来嵌套样式——推荐用deep(),这样样式不会污染全局。

(三)用第三方封装好的库快速上手:适合赶进度但又有一定需求的项目

如果你的项目赶进度,或者不想自己写太多插件配置,那可以直接用第三方封装好的Vue3 markdown-it库,比如vue3-markdown-itmd-editor-v3(这个是编辑器加渲染器二合一的)。

这里重点提一下vue3-markdown-it,它是直接基于markdown-it封装的Vue3组件,已经内置了一些常用的插件,比如GFM扩展、代码高亮(用的是Prism.js或者Highlight.js,可以自己选)、目录生成等,而且支持TypeScript,使用起来特别简单:

  1. 安装:npm install vue3-markdown-it或者yarn add vue3-markdown-it,如果你需要用内置的代码高亮,还要安装对应的依赖,比如Prism.js的话,还要安装prismjs和对应的语言包。
  2. 在全局或者局部引入组件:全局引入的话,在main.js或者main.ts里注册就行;局部引入的话,直接在需要的组件里import。
  3. 在模板里直接用,传content或者直接用插槽传内容,然后通过props或者插件配置来调整功能。

不过用第三方库也有缺点:比如功能可能不够灵活,插件的版本可能不是最新的,如果你有非常特殊的需求,可能还是得自己封装组件,所以赶进度的话用第三方库,有长期维护或者特殊需求的话,还是推荐自己封装。

高效使用markdown-it:常用插件的配置和自定义插件的入门

基础集成只是第一步,要让markdown-it好用,肯定得加插件,接下来讲几个最常用的插件怎么配置,还有怎么自己写一个简单的自定义插件。

(一)最常用的几个插件配置

  1. GFM扩展插件:也就是markdown-it-gfm(或者markdown-it-github?不对,更常用的是markdown-it-gfmmarkdown-it-task-lists配合?不,其实markdown-it-gfm已经包含了任务列表、表格、删除线、自动链接、围栏代码块这些GFM的核心特性,直接用它就行,安装好之后,在实例化markdown-it的时候,用.use()方法引入就行,比如const md = markdownIt().use(gfmPlugin)
  2. 代码高亮插件:有两个最常用的,一个是Prism.js,对应的插件是markdown-it-prism;另一个是Highlight.js,对应的插件是markdown-it-highlightjs,Prism.js更轻量,主题更多,而且支持行号显示、代码复制等扩展;Highlight.js支持的语言更多,配置更简单,我个人更推荐Prism.js,配合markdown-it-prismprismjs/plugins/toolbar/prism-toolbarprismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard这两个插件,体验会更好,配置的时候,除了引入markdown-it-prism,还要在main.js或者main.ts里引入Prism.js的核心库、主题CSS、你需要的语言包、还有工具栏和复制到剪贴板的插件CSS和JS。
  3. 目录生成插件:常用的是markdown-it-toc-done-right,这个插件可以根据markdown里的标题自动生成目录,支持自定义目录的层级、标签、锚点生成规则等,配置的时候,用.use()方法引入,可以传一个options对象,比如设置containerClasstoc-container,设置level为[1,2,3],这样只会生成h1到h3的目录。
  4. 数学公式渲染插件:常用的是markdown-it-katex,配合KaTeX库使用,渲染速度快,支持大部分LaTeX数学公式,安装好之后,还要引入KaTeX的核心库和主题CSS,配置的时候,用.use()方法引入,可以设置throwOnError为false,这样如果公式有错误,不会抛出异常,而是显示错误信息。
  5. 安全过滤插件:刚才提到过,推荐用DOMPurify配合markdown-it使用,具体步骤是:先安装DOMPurify,然后在实例化markdown-it的时候,不要把html设为true,而是在渲染完HTML字符串之后,用DOMPurify的sanitize()方法过滤一遍,再赋值给容器或者v-html,比如const html = md.render(content); const safeHtml = DOMPurify.sanitize(html);

(二)自定义插件入门:写一个简单的表情替换插件

有时候现成的插件满足不了你的需求,比如你想加一套自己项目的自定义表情,或者自己发明一个语法糖,这时候就得自己写插件了,markdown-it的插件系统其实很简单,它是基于规则链的,你可以在规则链的任意位置添加、修改、删除规则。

举个例子,我们写一个简单的表情替换插件,比如把smile:替换成😊,把heart:替换成❤️,把thumbsup:替换成👍,具体步骤是:

  1. 先创建一个插件函数,markdown-it的插件函数接收两个参数:第一个是markdown-it的实例md,第二个是插件的配置options(可选)。
  2. 在插件函数里,我们可以用md.renderer.rules或者md.core.ruler或者md.block.ruler或者md.inline.ruler来修改规则——表情替换属于inline规则,所以我们用md.inline.ruler.after或者md.inline.ruler.before来添加一个规则,或者直接修改md.renderer.rules.text规则?不对,修改text规则的话,效率可能更高,但如果有其他inline规则(比如链接、加粗),可能会有冲突,所以更稳妥的方法是用md.inline.ruler.after('link', 'emoji', emojiRule),在链接规则之后添加一个emoji规则。
  3. 然后定义emojiRule函数,这个函数接收两个参数:第一个是state(markdown-it的状态对象,包含了当前解析的内容、位置等信息),第二个是silent(是否静默模式,不渲染内容,只检查语法是否正确)。
  4. 在emojiRule函数里,我们先检查当前位置的字符是不是,如果不是,直接返回false;如果是,就继续往后找下一个,中间的内容就是表情的名称,比如smile、heart、thumbsup;然后检查这个表情名称是不是在我们的表情映射表里,如果是,就创建一个token,类型是text,内容是对应的emoji,然后更新state的位置,返回true;如果不是,就返回false。
  5. 在实例化markdown-it的时候,用.use()方法引入我们的自定义插件就行,还可以传一个表情映射表的options,这样插件更灵活。

Vue3集成markdown-it时的常见坑和解决方案

虽然markdown-it的集成和使用看起来很简单,但新手还是很容易踩坑,接下来讲几个最常见的坑和对应的解决方案。

(一)代码高亮不生效

代码高亮不生效是新手最常碰到的问题,原因有很多,我整理了几个最常见的:

  1. 没有引入代码高亮的主题CSS:很多人只安装了插件和语言包,忘了引入主题CSS,导致代码虽然被包裹了对应的标签,但没有颜色。
  2. 语言包没有引入或者引入错了:Prism.js和Highlight.js都需要单独引入你需要的语言包,比如你要渲染JavaScript代码,就要引入prismjs/components/prism-javascript或者highlight.js/lib/languages/javascript;如果语言名称写错了,比如把javascript写成了js,也不会生效。
  3. 围栏代码块的语言标识写错了:比如把js写成了javascript或者```JS,虽然有些插件支持大小写和别名,但最好还是用标准的语言名称,比如js、ts、html、css、python等。
  4. 插件的配置有问题:比如用markdown-it-prism的时候,没有正确配置preload选项,或者用markdown-it-highlightjs的时候,没有初始化Highlight.js。

解决方案就是逐一排查:先检查有没有引入主题CSS,再检查有没有引入正确的语言包,再检查围栏代码块的语言标识,最后检查插件的配置。

(二)Vue3的响应式数据更新后,markdown内容不重新渲染

如果你用自己封装的组件,而且用的是computed来处理内容,那正常情况下响应式数据更新后,内容会自动重新渲染;但如果你用的是mounted钩子函数里赋值innerHTML,那响应式数据更新后,内容是不会重新渲染的。

解决方案就是用watch或者watchEffect来监听content或者options的变化,一旦变化,就重新处理内容并赋值给innerHTML或者更新v-html绑定的变量。

(三)链接跳转有问题

默认情况下,markdown-it渲染出来的链接是直接打开的,如果你是在Vue3的单页应用(SPA)里,这样会导致页面刷新,体验不好;而且如果链接是本站的内部链接,应该用Vue Router的router-link或者router.push来跳转。

解决方案就是修改markdown-it的link_open规则,自定义链接的渲染方式:比如先判断链接的href是不是本站的内部链接,如果是,就创建一个router-link的token(不过markdown-it默认不支持创建Vue组件的token,所以更稳妥的方法是渲染成a标签,然后给a标签加一个class,比如internal-link,然后在组件的mounted或者updated钩子函数里,给所有.internal-link的a标签添加点击事件,阻止默认行为,然后用router.push来跳转;如果是外部链接,就加一个target="_blank"和rel="noopener noreferrer",防止安全问题)。

(四)图片加载失败

如果markdown里的图片是本地的,比如放在assets目录下,用Vite的静态资源导入语法直接读成字符串的话,图片的路径是正确的;但如果是用户输入的本地图片路径,或者是用了相对路径的外部图片,可能会加载失败。

解决方案就是:对于本地的固定图片,用Vite的静态资源导入语法;对于用户输入的图片,最好限制只能上传到服务器或者信任的图床,然后用绝对路径;如果确实需要用相对路径的外部图片,要确保路径是正确的,而且服务器支持跨域访问。

Vue3集成markdown-it其实并不难,核心步骤就是:选择合适的集成方式、配置常用的插件、解决常见的坑,纯静态场景用直接引入的方式,多个页面用通用组件封装,赶进度用第三方库;常用的插件有GFM扩展、代码高亮、目录生成、数学公式渲染、安全过滤等;自定义插件也很简单,只要掌握了markdown-it的规则链系统就行;常见的坑有代码高亮不生效、响应式数据不更新、链接跳转有问题、图片加载失败等,只要逐一排查就能解决。

markdown-it的功能远不止这些,你还可以用它做更多高级的事情,比如自定义语法糖、实现双向数据绑定的编辑器、导出PDF等,感兴趣的话可以去官方文档或者GitHub上看看更多的例子和插件。

版权声明

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

热门