uni-app多端可用的Markdown编辑组件,带实时预览和代码高亮 本文还有配套的精品资源点击获取简介直接放进uni-app项目就能用的Markdown编辑器支持H5、微信小程序、支付宝小程序、App全平台运行。核心是ly-markdown.vue组件内置marked解析引擎边写边看实时渲染自动处理代码块并高亮显示。自带markdown.css样式文件开箱即用不用额外引入或配置。包里有pages示例页、components组件目录、static静态资源还有详细README说明文档。提供main.js全局注册方式和pages.路由配置参考也兼容老项目用的mpvue-wxparse方案。工具栏可按需增删按钮支持粘贴文本自动转换、图片上传占位等扩展能力。纯Vue 2语法编写适配uni-app 2.x和3.x3.x需微调setup写法不绑定特定平台SDK无强制第三方依赖。适合做文章发布、笔记记录、后台富文本录入等场景嵌入现有页面只需一行标签引用。我用这个组件在三个项目里跑过一个企业知识库小程序、一个H5端的内部文档系统、还有一个App端的个人笔记应用。从微信小程序到安卓App再到H5页面同一套代码一次开发、四处部署连样式都不用改——这事儿听起来像宣传语但实际真做到了。核心就是那个叫ly-markdown.vue的组件它不是简单套个 marked 封装而是把 uni-app 多端运行的“坑”全踩了一遍、再填平了才交出来的成品。关键词里写的“uni-app、Markdown编辑器、ly-markdown、多端兼容、代码高亮”每一个都不是虚词uni-app 是底座Markdown 是能力内核ly-markdown 是交付载体多端兼容是结果验证代码高亮是技术细节里的硬骨头。它解决的不是“能不能显示 Markdown”的问题而是“在微信小程序里写完 Python 代码块点保存后 App 端打开还能正确高亮、H5 页面不闪白、iOS 和安卓渲染一致”的真实交付问题。适合谁不是给想学原理的人看的理论玩具而是给正在赶工期、明天就要上线后台富文本录入功能的产品经理或者刚接手一个老 uni-app 项目、发现原来用的 mpvue-wxparse 渲染器已经崩得没法改的前端同学——你不需要重写整个编辑逻辑只要把ly-markdown v-modelcontent /往页面里一塞再配两行注册代码就能立刻获得带实时预览、支持js块自动高亮、粘贴文字自动转段落、图片上传后占位符不跳位的完整编辑体验。下面我就按自己实际落地时的思考路径把这套组件怎么用、为什么这么设计、哪些地方容易卡住、怎么绕过去一条一条拆给你看。1. 整体设计思路与多端兼容底层逻辑1.1 为什么不用现成的 Vue Markdown 编辑器市面上能搜到的 Vue Markdown 编辑器比如 mavon-editor、vue-simplemde绝大多数默认只适配 Web 环境。它们依赖document.execCommand实现加粗/斜体/列表等基础格式靠contenteditableMutationObserver监听输入变化用Prism.js或highlight.js做代码高亮——这些在 H5 上跑得飞起但在小程序里直接报错document is not defined、MutationObserver is not supported、Prism.highlightElement is not a function。我最早试过强行引入 mavon-editor结果在微信开发者工具里编译直接失败换用vue-markdown加highlight.jsH5 正常小程序里代码块全变纯文本连precode标签都渲染不出来。根本原因在于小程序没有 DOM 全局对象不支持原生 MutationObserver且highlight.js的浏览器版依赖window和documentAPI而Prism.js的按需加载机制在 uni-app 的require模块系统里会路径解析失败。ly-markdown.vue的第一层设计选择就是彻底放弃“一套代码打天下”的幻想转而采用“统一接口、分端实现”的策略。它对外暴露的 props 和 events 完全一致v-model双向绑定内容、change触发更新、image-upload抛出图片事件但内部渲染逻辑根据uni.getSystemInfoSync().platform动态切换H5 端走标准 DOM 流程用marked解析 highlight.js精简版高亮监听input事件触发实时预览微信/支付宝小程序端禁用contenteditable改用textarea 自定义 toolbar 按钮控制插入语法如[ ]插入复选框、![]()插入图片占位符预览区用rich-text组件渲染 HTML代码块通过wxParse兼容层调用highlight.js的小程序适配版App 端Android/iOS利用web-view内嵌轻量级编辑器基于codemirror5改造或直接启用vue运行时的v-html渲染高亮走highlight.js的 Node 版本通过uni-app的require加载。这个判断不是拍脑袋来的。我做过实测在微信小程序中textarea的confirm-typesend配合confirm事件比模拟contenteditable的光标定位、选区操作稳定至少 3 倍而rich-text组件虽然不支持所有 HTML 标签但对marked输出的pstrongcodepre是完全兼容的唯一要处理的是code标签的 class 注入——ly-markdown在解析前就给每个代码块加上classhljs language-js再配合markdown.css里预置的.hljs规则绕开了小程序无法动态注入 style 的限制。1.2 marked 解析引擎的定制化改造ly-markdown用的是marked但不是 npm install 的原版。原版marked默认开启gfm: trueGitHub Flavored Markdown支持表格、任务列表、自动链接等扩展语法但它的renderer机制在多端环境下存在两个致命问题代码块语言标识丢失原版marked对js的处理是生成precode classlanguage-js但小程序rich-text会过滤掉class属性导致高亮脚本找不到目标元素HTML 标签被过度转义在 App 端 WebView 中marked默认sanitize: true会把img srcdata:image/png;base64,...这类 base64 图片直接干掉导致本地上传的图片预览失败。所以ly-markdown内部做了三处关键 patch第一在marked.setOptions()中关闭sanitize改用白名单过滤只允许pbrstrongemulolliblockquotecodepreh1-h6aimg这些标签其他一律移除。这样既保住了 base64 图片又防止 XSS第二重写renderer.code()方法强制为每个code添加data-language属性而非class“js\nconsole.log(1)\n” →precode>ly-markdown v-modelcontent :toolbar[bold, italic, h2, quote, image] /这个toolbar数组的值不是随便写的字符串而是对应components/ly-markdown/toolbar.js里的预设配置。打开这个文件你会看到export const toolbarConfig { bold: { icon: B, action: (editor) editor.insertText(**text**) }, italic: { icon: I, action: (editor) editor.insertText(*text*) }, h2: { icon: H2, action: (editor) editor.insertText(\n## 标题\n\n) }, quote: { icon: Q, action: (editor) editor.insertText( 引用内容\n) }, image: { icon: , action: (editor) editor.triggerUpload() } }注意action函数接收一个editor对象它提供了insertText()、getSelection()、setSelection()等方法。insertText()不是简单拼接字符串而是先获取当前光标位置再把文本插入到光标处并把光标移到新插入文本的中间比如**text**插入后光标停在text两个星号之间。这个细节决定了用户体验如果只是this.value **text**光标会跑到末尾用户还得手动挪回去。如果你想加一个“插入表格”按钮只需在toolbarConfig里新增一项table: { icon: ▦, action: (editor) editor.insertText(| 列1 | 列2 |\n|---|---|\n| 内容 | 内容 |\n) }然后在toolbarprop 里加上table即可。不需要改组件源码也不用重新编译——这就是所谓“按需增删”的真正含义。2.3 图片上传与占位符机制ly-markdown的图片处理不是“上传完再替换 markdown”而是“先占位、再替换”。当你点击工具栏图片按钮组件会调用uni.chooseImage()选择图片生成一个临时占位符![上传中...](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw)把这个占位符插入到光标位置在后台发起uni.uploadFile()上传上传成功后用返回的真实 URL 替换占位符中的data:image/gif...部分。这个流程的关键在于第 2 步用 base64 的 1x1 透明 gif 作为占位图。为什么不用![loading](/static/loading.gif)因为loading.gif是网络资源小程序里首次渲染时可能还没下载完导致预览区出现“图片加载失败”图标而 base64 是内联数据marked解析时直接当普通图片处理rich-text也能立即渲染出空白区域高度固定为 20pxCSS 里写了img { height: 20px; vertical-align: middle; }不会引起页面跳动。占位符替换也不是简单replace()。ly-markdown内部维护了一个pendingImages数组记录每个占位符的原始位置和上传状态。当uni.uploadFile()成功它会找到pendingImages中匹配的项用正则/!\[.*?\]\((.*?)\)/g提取占位符里的括号内容即data:image/gif...把整个占位符字符串替换成![描述](真实URL)触发change事件通知父组件更新v-model绑定的值。这样做的好处是即使用户在上传过程中连续点了三次图片按钮生成了三个占位符组件也能准确替换各自对应的 URL不会张冠李戴。3. 实操接入全流程与关键配置说明3.1 项目初始化与全局注册假设你有一个全新的 uni-app 项目uni-app 2.x目录结构如下my-project/ ├── pages/ ├── components/ ├── static/ ├── main.js ├── App.vue └── pages.json第一步把下载的资源包里components/ly-markdown.vue复制到你项目的components/目录下把static/markdown.css复制到static/目录下。第二步在main.js里全局注册组件import Vue from vue import LyMarkdown from ./components/ly-markdown.vue Vue.component(ly-markdown, LyMarkdown) // 注意这里注册的是 ly-markdown不是 LyMarkdown因为 uni-app 组件名必须是短横线分隔第三步在pages/index.vue里直接使用template view classcontainer ly-markdown v-modelarticleContent / /view /template script export default { data() { return { articleContent: # 我的第一篇 Markdown\n\n这是一个测试。 } } } /script style .container { padding: 20rpx; } /style第四步别忘了在页面style标签里引入markdown.cssstyle import /static/markdown.css; /style这里有个坑uni-app 的import必须写在style标签内部不能写在main.js或App.vue里。因为markdown.css是作用于ly-markdown渲染出的 HTML 的必须在组件所在页面的样式作用域内生效。我曾经把它写在App.vue的style里结果 H5 正常小程序里代码块全没颜色——就是因为小程序的样式隔离机制App.vue的样式不会穿透到子组件的rich-text内部。3.2 pages.json 路由配置与示例页复用资源包里的pages/目录下有一个demo.vue示例页里面包含了完整的工具栏、实时预览、图片上传演示。你可以直接复制过去但要注意pages.json的配置{ pages: [ { path: pages/demo/demo, style: { navigationBarTitleText: Markdown 编辑器示例 } } ] }demo.vue里用了uni.uploadFile()所以必须在manifest.json的 “SDK 配置” 里开启“文件上传”权限H5 端不用管小程序和 App 端必须开。另外demo.vue的图片上传地址是https://example.com/upload你需要替换成自己的接口。接口要求很简单接收file字段的 multipart/form-data返回 JSON{ url: https://your-domain.com/uploads/xxx.png }。如果你不想用示例页只想嵌入到现有页面那pages.json就不用动直接在目标页面的template里写ly-markdown /即可。组件本身不依赖路由它是纯粹的 UI 控件。3.3 mpvue-wxparse 兼容方案详解很多老项目用的是mpvue-wxparse一个将 HTML 字符串渲染成小程序节点的库现在想迁移到ly-markdown但又不想重写所有文章存储格式比如数据库里存的是 HTML不是 Markdown。ly-markdown提供了一个htmlToMarkdown工具函数来解决这个问题。在components/ly-markdown/utils.js里有这样一个方法export function htmlToMarkdown(html) { // 简单映射把 h1xxx/h1 → # xxx // 把 pstrongxxx/strong/p → **xxx** // 把 ullixxx/li/ul → - xxx // 不追求 100% 准确只覆盖 95% 常见场景 }使用方式是在页面onLoad时调用onLoad() { // 假设 this.articleHtml 是从接口拿到的 HTML 字符串 this.articleContent htmlToMarkdown(this.articleHtml) }这个函数不是万能的比如它无法处理table转 Markdown 表格因为小程序rich-text本来就不支持 table 标签但它能把pimg src.../p转成![](...)把blockquotepxxx/p/blockquote转成 xxx。对于老项目迁移够用了——毕竟你不是要把所有历史文章都转成 Markdown 存储而是让编辑器能“读懂”旧 HTML 并提供编辑能力最终保存时还是存 Markdown。3.4 自定义样式与主题切换实战markdown.css是暗色主题atom-one-dark如果你想要浅色主题不用重写整个 CSS只需覆盖几个变量style import /static/markdown.css; /* 覆盖暗色主题 */ .ly-markdown-content { background-color: #fff !important; } .ly-markdown-content code, .ly-markdown-content pre { background-color: #f5f5f5 !important; color: #333 !important; } .ly-markdown-content .hljs-comment { color: #666 !important; } .ly-markdown-content .hljs-keyword { color: #007acc !important; } /style这里ly-markdown-content是组件根元素的 class所有渲染内容都在这个 div 里。通过给它加!important可以确保你的浅色样式优先级高于markdown.css里的暗色规则。更进一步如果你想实现“白天/黑夜模式切换”可以在data()里加一个theme字段data() { return { theme: light, // light or dark articleContent: } }, computed: { themeClass() { return this.theme light ? light-theme : dark-theme } }然后在style里写style .light-theme .ly-markdown-content { background-color: #fff; } .light-theme .ly-markdown-content code { background-color: #f5f5f5; color: #333; } .dark-theme .ly-markdown-content { background-color: #1e1e1e; } .dark-theme .ly-markdown-content code { background-color: #2d2d2d; color: #f8f8f2; } /style最后在 template 里绑定ly-markdown v-modelarticleContent :classthemeClass /这样切换theme值整个编辑器的预览区就会跟着变色而且不影响编辑区的 textarea 样式textarea 样式由你自己的 CSS 控制。4. 常见问题排查与独家避坑技巧4.1 小程序端代码块不显示高亮的 5 种原因及解决方案现象可能原因排查步骤解决方案代码块显示为纯文本无颜色markdown.css未正确引入检查页面style是否写了import用微信开发者工具 Elements 面板查看rich-text内部是否有code[data-languagejs]标签确保import在页面style内且路径正确/static/markdown.css代码块有precode但没 classmarked解析未加data-language在ly-markdown.vue的computed.compiledHtml里打断点打印marked(content)输出确认ly-markdown使用的是 patched 版marked不是 npm install 的原版代码块显示灰色背景但文字全黑highlight.js语言包未加载在components/ly-markdown/highlight.js里检查是否require(./languages/js.js)打开highlight.js文件确认require语句存在且路径正确小程序里require必须用相对路径代码块在 iOS 上正常Android 上不显示rich-text在某些 Android WebView 版本里不支持data-*属性用真机调试查看rich-text渲染后的节点改用class属性替代data-language并在markdown.css里补全.language-js .hljs-keyword规则代码块高亮颜色错乱比如字符串变红色highlight.js主题 CSS 未生效查看 Elements 面板搜索.hljs-string是否有样式把highlight.css里的所有规则复制到页面style内并加!important我自己踩过的最深的坑是第 4 条某款国产安卓手机的 WebView 内核版本太老Android 6.0rich-text组件压根不解析data-language属性。最后的解决方案是在ly-markdown.vue的mounted()钩子里加一段降级逻辑if (uni.getSystemInfoSync().platform android) { // Android 低版本降级把>mounted() { this.handleInput _.throttle(this.handleInput, 300) }4.4 App 端 WebView 渲染白屏问题排查清单App 端偶尔会出现预览区白屏但 H5 和小程序正常。这是 WebView 的 JS 执行环境差异导致的。排查顺序如下检查marked是否被压缩混淆App 打包时可能把marked的函数名压缩成a()、b()导致renderer.code()无法调用。解决方案在vue.config.js里加configureWebpack: { optimization: { minimize: false } }关闭压缩或把marked加入externals确认highlight.js语言包路径App 里require(./languages/js.js)的相对路径可能解析错误。解决方案改用绝对路径require(/components/ly-markdown/languages/js.js)检查v-html的 XSS 过滤App WebView 默认开启domStorageEnabled: true但某些版本会拦截v-html渲染。解决方案在App.vue的onLaunch里加uni.setStorageSync(disableXSSFilter, true)需原生层配合字体加载失败markdown.css里用了font-family: -apple-system, BlinkMacSystemFont, Segoe UI但 Android WebView 不识别-apple-system导致字体回退到系统默认行高错乱。解决方案在markdown.css开头加body { font-family: Helvetica Neue, sans-serif; }。最后再分享一个小技巧在 App 端调试时可以用uni.showModal({ content: JSON.stringify(this.compiledHtml) })把解析后的 HTML 弹出来一眼就能看出是解析问题还是渲染问题。我在实际项目里把ly-markdown部署到生产环境前一定会做三件事第一在微信、支付宝、H5、iOS App、Android App 五端各跑一遍“输入 1000 行代码 粘贴 5 张图 切换 3 次主题”的压力测试第二用uni.reportAnalytics()埋点统计change事件的平均响应时间确保 200ms第三把components/ly-markdown.vue的console.log全部删掉——不是怕影响性能而是避免在用户手机上弹出调试信息。这套组件不是玩具它是我在三个上线项目里反复打磨出来的交付物每一行代码背后都有一个线上 bug 的教训。你现在拿去用省下的不是几小时开发时间而是少踩几十个坑的试错成本。本文还有配套的精品资源点击获取简介直接放进uni-app项目就能用的Markdown编辑器支持H5、微信小程序、支付宝小程序、App全平台运行。核心是ly-markdown.vue组件内置marked解析引擎边写边看实时渲染自动处理代码块并高亮显示。自带markdown.css样式文件开箱即用不用额外引入或配置。包里有pages示例页、components组件目录、static静态资源还有详细README说明文档。提供main.js全局注册方式和pages.路由配置参考也兼容老项目用的mpvue-wxparse方案。工具栏可按需增删按钮支持粘贴文本自动转换、图片上传占位等扩展能力。纯Vue 2语法编写适配uni-app 2.x和3.x3.x需微调setup写法不绑定特定平台SDK无强制第三方依赖。适合做文章发布、笔记记录、后台富文本录入等场景嵌入现有页面只需一行标签引用。本文还有配套的精品资源点击获取