使用npm创建项目
Vue3项目里要加Markdown编辑/预览功能,选哪个开源库?怎么快速搭建?
前阵子帮朋友做一个轻量级技术博客的后台,他指定用Vue3+Vite,核心需求就是一个带实时预览、一键导出图片/HTML、代码高亮、支持数学公式渲染的编辑器,一开始我以为随便找个热门的Vue2兼容库改改就行,结果踩了不少坑:要么是Vue3版本还在早期测试不稳定,要么是数学公式、图片上传这些插件生态崩得离谱,要么是文档全是英文且更新慢,折腾了三天,最终选了一套组合拳,不仅完美覆盖所有需求,还能自由定制界面和功能,现在后台上线快一个月了,朋友连说好用,今天就把选库的逻辑、踩过的雷、快速搭建的步骤,还有进阶自定义的小技巧全讲清楚,保证不管是刚入门Vue3的小白,还是要快速迭代功能的开发者都能用得上。
先搞懂:你的项目到底需要什么样的Vue3 Markdown编辑器?
别上来就搜“Vue3 Markdown编辑器排行榜”然后选第一,每个开源库的定位不一样,盲目跟风只会浪费时间,先花5分钟列个需求清单,
- 核心能力边界:是只需要预览渲染静态Markdown就行,还是要带可拖拽编辑、大纲导航、撤销重做的编辑器?
- 技术栈适配度:项目是用Vue3 Options API还是Composition API?有没有搭配Tailwind CSS、Element Plus这些UI库?要不要支持TypeScript?
- 插件生态完整性:要不要数学公式(KaTeX/MathJax)、流程图(Mermaid)、代码高亮(Prism/Shiki)、图片上传(本地/OSS)、一键导出这些附加功能?
- 可定制性要求:是直接拿UI来用就好,还是要完全重写工具栏、编辑器布局,甚至修改Markdown解析规则?
- 性能与体积:是追求极致轻量(比如只渲染核心语法,体积几KB),还是功能全一点、体积稍大也没关系?
根据这几个维度,我把目前常用且稳定的Vue3 Markdown开源库分成了三类,大家可以对号入座:
第一类:轻量无UI,专注解析与渲染
适合只需要展示Markdown内容,或者想要100%自定义编辑器UI的开发者,核心就是用底层的Markdown解析库,搭配Vue3的组件化能力自己拼。
常用库有哪些?
- Marked:解析速度最快的库之一,体积小(压缩后不到20KB),支持HTML标签嵌入,但代码高亮、数学公式这些都需要自己加插件。
- Remark + Rehype:属于Unified生态的工具链,完全基于插件体系,功能可扩展性极强,但学习曲线稍微陡一点,体积比Marked大一些(核心库加基础插件大概30-50KB)。
第二类:带基础UI,开箱即用但可扩展
适合大多数普通开发者,核心需求是“快速上线+按需加小功能”,这些库已经封装好了基础的编辑器界面、实时预览、撤销重做,只需要引入自己需要的插件就行。
常用库有哪些?
- Vditor:这个是我朋友那个项目最终用的解析渲染底层的核心备选之一?不,备选是Editor.md,不过Vditor更棒!它是专门为中文场景优化的,文档全中文,功能超级全:自带大纲导航、任务列表、表情选择、代码高亮(支持Prism和Shiki)、数学公式(KaTeX)、流程图(Mermaid)、本地图片拖拽上传、一键导出Markdown/HTML/PDF,而且支持Vue2/Vue3、React、原生JS,甚至小程序!可定制性也不错,可以通过配置项隐藏不需要的工具栏按钮,或者自己重写某个工具栏组件,体积的话,完整版压缩后大概1.2MB,如果只加载核心语法和基础插件,大概300KB,完全可以接受。
- Vue-Markdown-It-Vite:基于Markdown-It封装的Vue3组件,带一个极简的实时预览编辑器(分屏式),支持TypeScript,可通过Markdown-It的插件生态扩展功能,比如代码高亮、数学公式、流程图,文档全英文,但不算复杂,适合习惯Markdown-It的开发者,体积比Vditor小很多,完整版大概200KB左右。
- Milkdown:这个库很有意思,采用了“无头UI”+“编辑器框架”的思路,核心库不带任何UI组件,所有界面元素(包括工具栏、编辑器、预览区)都需要自己写或者用官方提供的UI插件包,可定制性几乎是无限的,适合要做和现有项目UI风格完全统一的编辑器,它基于ProseMirror和Remark,插件生态也比较丰富,支持TypeScript,体积大概500KB左右(核心库加基础UI插件),不过学习曲线稍微比Vditor和Vue-Markdown-It-Vite陡一点,因为要理解它的“Node/Schema/Mark”模型。
第三类:功能极全,类似Typora所见即所得(WYSIWYG)
适合对编辑体验要求极高的项目,比如笔记类应用、协作文档类应用,这些库已经完全封装了WYSIWYG的编辑逻辑,用户可以像编辑Word一样编辑Markdown,不需要切换预览区。
常用库有哪些?
- Bytemd:字节跳动开源的WYSIWYG Markdown编辑器,支持Vue2/Vue3、React、原生JS,文档全中文,功能和Typora很像:实时预览、大纲导航、代码高亮(支持Prism和Shiki)、数学公式(KaTeX)、流程图(Mermaid)、本地图片拖拽上传、一键导出,还支持Vue3的Composition API和TypeScript,可定制性也不错,可以通过配置项调整UI,或者自己写插件,体积大概1MB左右。
- Quill + Markdown Shortcuts:Quill本身是一个富文本编辑器,不是专门的Markdown编辑器,但可以通过Markdown Shortcuts插件实现Markdown语法的快捷输入,然后通过Quill的Delta格式转成Markdown输出,适合已经在用Quill做富文本编辑,又想加一点Markdown功能的项目。
选对了库,怎么快速搭建一个Vue3+Vite+Vditor的Markdown编辑器?
刚才说了,朋友的项目最终用的是Vditor,因为它的中文文档、中文社区支持(虽然不算特别大,但有问题基本能在GitHub Issues或者Gitee Issues里找到答案)、功能完整性、开箱即用的体验都是最好的,接下来就以Vditor为例,给大家演示从0到1快速搭建一个带实时预览、代码高亮、数学公式、本地图片上传、一键导出的Markdown编辑器,代码量非常少,大概10分钟就能搞定。
第一步:创建Vue3+Vite项目
如果已经有项目了,这一步可以跳过,打开终端,输入以下命令:
# 或者使用yarn yarn create vite vue3-vditor-demo --template vue-ts # 或者使用pnpm pnpm create vite vue3-vditor-demo --template vue-ts # 进入项目目录 cd vue3-vditor-demo # 安装依赖 npm install # 或者yarn yarn install # 或者pnpm pnpm install
这里我选的是TypeScript模板,因为TypeScript能提供更好的类型提示和代码维护性,不过用JavaScript模板也是一样的,只是不需要处理类型定义的问题。
第二步:安装Vditor依赖
打开终端,输入以下命令:
npm install vditor # 或者yarn yarn add vditor # 或者pnpm pnpm add vditor
Vditor的依赖非常少,只有几个核心的工具库,安装起来很快。
第三步:在Vue3组件中引入Vditor
打开src/components目录,创建一个新的组件文件VditorEditor.vue,然后输入以下代码:
<template>
<div class="vditor-container">
<!-- Vditor会自动挂载到这个div上 -->
<div ref="vditorRef"></div>
<!-- 一键导出按钮 -->
<div class="export-buttons">
<button @click="exportMarkdown">导出Markdown</button>
<button @click="exportHtml">导出HTML</button>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, onBeforeUnmount } from 'vue'
import Vditor from 'vditor'
import 'vditor/dist/index.css'
// 定义Vditor实例的引用
const vditorRef = ref<HTMLDivElement>()
// 定义Vditor实例
let vditor: Vditor | null = null
onMounted(() => {
if (!vditorRef.value) return
// 初始化Vditor
vditor = new Vditor(vditorRef.value, {
// 配置Vditor的高度
height: 600,
// 配置Vditor的模式,这里用的是分屏模式(wysiwyg是所见即所得,ir是即时渲染)
mode: 'sv',
// 配置Vditor的缓存,这里关闭了本地缓存,避免刷新页面后内容残留
cache: {
enable: false
},
// 配置Vditor的预览,开启了数学公式、流程图、代码高亮
preview: {
math: {
enable: true,
engine: 'KaTeX'
},
mermaid: {
enable: true
},
hljs: {
enable: true,
style: 'github'
}
},
// 配置Vditor的工具栏,这里只保留了常用的按钮
toolbar: [
'emoji',
'headings',
'bold',
'italic',
'strike',
'link',
'|',
'list',
'ordered-list',
'check',
'outdent',
'indent',
'|',
'quote',
'line',
'code',
'inline-code',
'insert-before',
'insert-after',
'|',
'upload',
'record',
'table',
'math',
'mermaid',
'|',
'undo',
'redo',
'|',
'fullscreen',
'edit-mode',
'content-theme',
'code-theme',
'preview',
'info',
'help'
],
// 配置Vditor的图片上传,这里用的是本地临时上传,实际项目中要换成OSS上传
upload: {
accept: 'image/*',
multiple: true,
fieldName: 'file',
max: 10 * 1024 * 1024,
url: '/api/upload',
// 本地临时上传的模拟函数
handler(files: File[]) {
return new Promise((resolve) => {
const urls: string[] = []
files.forEach((file) => {
const reader = new FileReader()
reader.onload = (e) => {
if (e.target?.result) {
urls.push(e.target.result as string)
if (urls.length === files.length) {
// 这里需要注意,Vditor的upload handler返回的Promise的resolve参数格式是{ data: { errFiles: [], succMap: { [filename: string]: url } } }
const succMap: Record<string, string> = {}
files.forEach((file, index) => {
succMap[file.name] = urls[index]
})
resolve({
data: {
errFiles: [],
succMap
}
})
}
}
}
reader.readAsDataURL(file)
})
})
}
}
})
})
// 导出Markdown
const exportMarkdown = () => {
if (!vditor) return
// 获取编辑器的Markdown内容
const content = vditor.getValue()
// 创建一个Blob对象
const blob = new Blob([content], { type: 'text/markdown' })
// 创建一个a标签
const a = document.createElement('a')
// 设置a标签的href
a.href = URL.createObjectURL(blob)
// 设置a标签的下载文件名
a.download = 'document.md'
// 点击a标签
a.click()
// 释放URL对象
URL.revokeObjectURL(a.href)
}
// 导出HTML
const exportHtml = () => {
if (!vditor) return
// 获取编辑器的HTML内容
const content = vditor.getHTML()
// 生成一个完整的HTML文档
const fullHtml = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">Document</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vditor/dist/index.css">
</head>
<body>
<div class="vditor-reset">${content}</div>
</body>
</html>
`
// 创建一个Blob对象
const blob = new Blob([fullHtml], { type: 'text/html' })
// 创建一个a标签
const a = document.createElement('a')
// 设置a标签的href
a.href = URL.createObjectURL(blob)
// 设置a标签的下载文件名
a.download = 'document.html'
// 点击a标签
a.click()
// 释放URL对象
URL.revokeObjectURL(a.href)
}
onBeforeUnmount(() => {
// 组件卸载前销毁Vditor实例,避免内存泄漏
vditor?.destroy()
})
</script>
<style scoped>
.vditor-container {
width: 100%;
max-width: 1200px;
margin: 20px auto;
}
.export-buttons {
margin-top: 20px;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.export-buttons button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #409eff;
color: white;
cursor: pointer;
font-size: 14px;
}
.export-buttons button:hover {
background-color: #66b1ff;
}
</style>
这段代码虽然看起来有点长,但大部分都是Vditor的配置项,非常容易理解,我已经加了详细的注释,大家可以根据自己的需求修改配置项,比如调整高度、模式、缓存、预览、工具栏、图片上传等等。
第四步:在App.vue中引入VditorEditor组件
打开src/App.vue,替换成以下代码:
<template>
<div class="app-container">
<h1>Vue3+Vite+Vditor Markdown编辑器演示</h1>
<VditorEditor />
</div>
</template>
<script setup lang="ts">
import VditorEditor from './components/VditorEditor.vue'
</script>
<style scoped>
.app-container {
width: 100%;
min-height: 100vh;
background-color: #f5f7fa;
padding-bottom: 40px;
}
h1 {
text-align: center;
padding-top: 40px;
color: #333;
font-size: 28px;
}
</style>
第五步:启动项目测试
打开终端,输入以下命令:
npm run dev # 或者yarn yarn dev # 或者pnpm pnpm dev
然后在浏览器中打开终端输出的地址(一般是http://localhost:5173/),你就会看到一个功能完整的Markdown编辑器了!可以试试输入一些Markdown语法,比如标题、列表、代码块、数学公式、流程图,也可以试试拖拽本地图片上传,或者点击一键导出按钮导出Markdown/HTML文件。
进阶:怎么自定义Vditor的界面和功能?
刚才的演示只是用了Vditor的基础配置,如果你想要更符合自己项目UI风格的编辑器,或者想要加一些特殊功能,比如支持自定义表情、支持协作编辑、支持保存到数据库等等,Vditor也完全能满足你的需求,这里给大家讲几个常用的进阶技巧:
完全重写Vditor的工具栏
Vditor允许你通过配置项toolbar传入一个自定义的数组,数组中的元素可以是字符串(对应Vditor内置的工具栏按钮),也可以是一个对象(对应自定义的工具栏按钮),你想要加一个“保存到数据库”的按钮,可以这样修改配置项:
toolbar: [
// 内置按钮
'emoji',
'headings',
'bold',
// 自定义按钮
{
name: 'save',
tipPosition: 'n',
tip: '保存到数据库',
className: 'vditor-toolbar-save',
icon: '<svg t="1699999999999" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12345"><path d="M512 64a448 448 0 1 0 448 448A448 448 0 0 0 512 64z m0 832a384 384 0 1 1 384-384 384 384 0 0 1-384 384z" fill="#ffffff"></path><path d="M512 448a64 64 0 0 0-64 64v128a64 64 0 0 0 128 0V512a64 64 0 0 0-64-64z" fill="#ffffff"></path><path d="M512 320a32 32 0 1 0 32 32 32 32 0 0 0-32-32z" fill="#ffffff"></path></svg>',
click() {
if (!vditor) return
const content = vditor.getValue()
// 这里写保存到数据库的逻辑
console.log('保存到数据库的内容:', content)
alert('保存成功!')
}
},
// 其他内置按钮
'|',
'undo',
'redo'
]
然后在style标签中加一点自定义按钮的样式:
.vditor-toolbar-save {
background-color: #67c23a !important;
}
.vditor-toolbar-save:hover {
background-color: #85ce61 !important;
}
修改Vditor的主题
Vditor自带了几个编辑器主题和代码高亮主题,你可以通过配置项theme和preview.hljs.style来修改,编辑器主题可以选classic、dark、ant-design,代码高亮主题可以选github、atom-one-dark、dracula、monokai等等,如果你想要完全自定义主题,可以参考Vditor的CSS源码,自己写一套CSS文件,然后引入到项目中。
监听Vditor的事件
Vditor提供了很多事件,比如input时触发)、focus(编辑器获得焦点时触发)、blur(编辑器失去焦点时触发)、keydown(按下键盘时触发)等等,你可以通过配置项after、input、focus、blur、keydown来监听这些事件,你想要在输入内容时自动保存草稿到本地存储,可以这样修改配置项:
input() {
if (!vditor) return
const content = vditor.getValue()
localStorage.setItem('vditor-demo-draft', content)
}
然后在初始化Vditor的时候,从本地存储中读取草稿:
onMounted(() => {
if (!vditorRef.value) return
// 从本地存储中读取草稿
const draft = localStorage.getItem('vditor-demo-draft') || ''
vditor = new Vditor(vditorRef.value, {
// 其他配置项
value: draft,
// 其他配置项
})
})
踩过的雷,帮你避坑
刚才也说了,一开始我踩了不少坑,现在把这些雷总结出来,帮大家少走弯路:
- 不要用Vue2的兼容库强行改Vue3:比如Editor.md,虽然有Vue2的封装,但Vue3的版本非常老,而且数学公式、图片上传这些插件都不兼容,最后我花了两天时间才放弃。
- 注意Vditor的版本号:Vditor的更新速度比较快,有时候新版本会有一些小bug,建议大家用稳定版本,比如目前(2024年11月)的稳定版本是
10.9。 - 本地临时上传的图片刷新页面后会消失:这个是正常的,因为本地临时上传的图片是用Base64编码的,没有保存到服务器,实际项目中一定要换成OSS上传,比如阿里云OSS、腾讯云COS、七牛云OSS等等。
- 导出PDF功能需要额外的依赖:Vditor的导出PDF功能是基于jsPDF和html2canvas实现的,如果你需要这个功能,要额外安装这两个依赖:
npm install jspdf html2canvas
然后在组件中引入:
import jsPDF from 'jspdf' import html2canvas from 'html2canvas'
最后写导出PDF的函数。
- Milkdown的学习曲线稍微陡一点:如果你是第一次接触无头UI和ProseMirror,可能需要花一点时间学习它的“Node/Schema/Mark”模型,不过学会之后,可定制性真的是无限的。
今天给大家讲了Vue3 Markdown编辑器的选库逻辑、踩过的雷、快速搭建Vditor的步骤,还有几个常用的进阶技巧,如果你只是需要快速上线一个带基础功能的Markdown编辑器,选Vditor肯定没错;如果你想要100%自定义编辑器UI,选Remark+Rehype或者Milkdown;如果你想要类似Typora的WYSIWYG体验,选Bytemd,希望这篇文章能帮到大家,如果有什么问题,欢迎在评论区留言讨论!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网



