小程序 Markdown 渲染血泪史:mp-html 组件从入门到放弃再到重生
前言:小程序 MD 渲染的难点
在小程序开发中,Markdown 内容的渲染一直是一个让人头疼的问题。与 Web 端丰富的生态不同,小程序平台有着诸多限制:
- 平台差异性:微信小程序、支付宝小程序、百度小程序等平台对 HTML 和 CSS 的支持程度不同
-
安全限制:小程序不允许直接使用
dangerouslySetInnerHTML或动态执行脚本 - 样式兼容性:部分 CSS 属性在小程序中不被支持
- 性能瓶颈:复杂的富文本渲染容易影响页面性能
- 交互限制:图片预览、链接跳转等交互行为需要特殊处理
这些限制使得在小程序中实现完整的 Markdown 渲染变得异常困难。开发者往往需要在功能完整性和性能体验之间寻找平衡。
最近的踩坑经历
最近在开发一个小程序时,需要在小程序中渲染包含代码块、表格、图片等多种元素的 Markdown 内容。一开始天真地以为可以用简单的 HTML 解析器搞定,结果踩了一堆坑。
坑一:代码高亮完全不工作
最先遇到的就是代码高亮问题。我按照文档启用 Markdown 插件后,代码块倒是能显示,但是语法高亮完全没有效果。
// 这是我一开始的配置
<mp-html
:content="markdownContent"
:markdown="true"
/>
代码块显示成了普通文本,完全没有颜色区分。后来才发现需要单独启用 highlight 插件,而且还需要重新构建组件。
坑二:多语言代码块识别失败
启用代码高亮后,发现只有 JavaScript 代码能正常高亮,其他语言如 Java、Python、Go 等都显示为纯文本。原来 mp-html 默认只支持基础的几种语言,需要手动下载完整的 Prism.js 文件来支持更多编程语言。
坑三:图片路径和预览问题
Markdown 中的图片链接在小程序中无法直接显示。一开始我尝试使用相对路径,结果全部加载失败。后来发现需要配置 domain 属性来处理图片路径,而且图片预览功能还需要手动开启。
坑四:表格滚动和样式问题
复杂的表格在小程序中显示不全,内容会被截断。特别是手机端,表格列数多时根本看不清内容。而且表格的样式也需要特殊处理,默认样式很难看。
坑五:文本选择和复制功能
用户希望能复制代码块的内容,但是默认情况下小程序不支持文本选择。后来发现可以通过配置 selectable 属性来开启这个功能。
坑六:Mermaid 图表渲染失败
最棘手的问题是 Mermaid 图表渲染。在 Markdown 中,Mermaid 图表通常以代码块形式存在:
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
mp-html 组件本身不支持 Mermaid 语法渲染,因为 Mermaid 需要 JavaScript 引擎来解析和绘制图表,而小程序环境对这有限制。
一开始我尝试直接在 Markdown 中嵌入 Mermaid 代码,但结果只能显示为普通的代码块,完全没有图表效果。
## 解决方案大揭秘
经过一番折腾,终于找到了完整的解决方案。以下是我的实战配置:
### 1. 完整的组件配置
```vue
<template>
<view class="article-container">
<mp-html
:content="articleContent"
:markdown="true"
:preview-img="true"
:scroll-table="true"
:selectable="true"
:use-anchor="true"
:lazy-load="true"
container-style="padding: 20rpx; background: #fff; border-radius: 12rpx;"
@load="onContentLoad"
@ready="onContentReady"
@imgtap="onImageTap"
@linktap="onLinkTap"
@error="onError"
/>
</view>
</template>
2. 插件配置(重点)
需要在 node_modules/mp-html/tools/config.js 中启用所有必要的插件:
module.exports = {
plugins: [
'markdown', // Markdown 解析
'highlight', // 代码高亮
'emoji', // Emoji 支持
],
// 全局样式配置
externStyle: `
.markdown-body {
color: #333;
line-height: 1.6;
font-size: 28rpx;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3 {
color: #2c3e50;
margin: 40rpx 0 20rpx 0;
font-weight: 600;
}
.markdown-body p {
margin: 20rpx 0;
text-align: justify;
}
.markdown-body pre {
background: #f6f8fa;
border-radius: 8rpx;
padding: 20rpx;
overflow-x: auto;
}
.markdown-body code {
background: #f1f3f4;
padding: 4rpx 8rpx;
border-radius: 4rpx;
font-family: 'Consolas', 'Monaco', monospace;
}
.markdown-body blockquote {
border-left: 4rpx solid #ddd;
padding-left: 20rpx;
margin: 20rpx 0;
color: #666;
background: #fafafa;
}
.markdown-body table {
border-collapse: collapse;
width: 100%;
margin: 20rpx 0;
}
.markdown-body th,
.markdown-body td {
border: 1rpx solid #ddd;
padding: 12rpx;
text-align: left;
}
.markdown-body th {
background: #f5f5f5;
font-weight: 600;
}
`
}
3. 代码高亮扩展(关键步骤)
要支持多种编程语言的代码高亮,需要:
- 访问 prismjs.com/download.ht…
- 选择 Tomorrow Night 主题
- 勾选需要的语言:JavaScript, Java, Python, Go, C++, C, HTML, CSS 等
- 下载
prism.min.js和prism.css - 替换
node_modules/mp-html/plugins/highlight/目录下的对应文件 - 重新构建组件:
npm run build:uni-app
4. 代码高亮配置优化
// plugins/highlight/config.js
module.exports = {
copyByLongPress: true, // 长按复制代码
showLanguageName: true, // 显示语言名称
showLineNumber: true // 显示行号
}
5. Mermaid 图表处理
对于 Mermaid 图表渲染问题,需要在前端预处理,将 Mermaid 代码块转换为图片:
// utils/mermaidProcessor.js
export function processMermaidInMarkdown(content) {
if (!content) return content
// 匹配 mermaid 代码块
const mermaidRegex = /```mermaid\s*\n([\s\S]*?)\n```/g
return content.replace(mermaidRegex, (match, mermaidCode) => {
try {
// 将 mermaid 代码发送到服务器生成图片
// 这里需要实现一个服务,将 mermaid 代码转换为图片 URL
const imageUrl = generateMermaidImageUrl(mermaidCode)
return ``
} catch (error) {
console.error('Mermaid 处理失败:', error)
// 处理失败时返回原始代码块
return match
}
})
}
// 在 Vue 组件中使用
const processedContent = computed(() => {
const content = question.value?.content
if (!content) return ''
// 先处理 mermaid(将 mermaid 代码块转换为图片)
const mermaidProcessed = processMermaidInMarkdown(content)
// 再转换为 HTML
return mermaidProcessed
})
关键点:
- Mermaid 图表无法在小程序中直接渲染,需要预先转换为图片
- 需要后端服务或第三方 API 将 Mermaid 代码转换为图片
- 处理顺序:先转换 Mermaid → 再解析 Markdown → 最后渲染 HTML
6. 图片处理策略
// 处理图片路径
getImageUrl(originalUrl) {
// 如果是相对路径,拼接完整域名
if (originalUrl.startsWith('/')) {
return `https://your-domain.com${originalUrl}`
}
// 如果是外部链接,可以添加代理或直接返回
return originalUrl
}
6. 事件处理
export default {
methods: {
onContentLoad() {
console.log('Markdown 内容加载完成')
// 可以在这里添加加载完成后的逻辑
},
onContentReady() {
console.log('所有资源加载完成')
// 图片等资源加载完成后的处理
},
onImageTap(e) {
const { src, i } = e.detail
console.log(`点击第 ${i + 1} 张图片:`, src)
// 可以在这里添加图片统计或其他逻辑
},
onLinkTap(e) {
const { href } = e.detail
// 处理内部链接跳转
if (href.startsWith('#')) {
// 锚点跳转
this.$refs.mpHtml.navigateTo(href.substring(1))
} else if (href.startsWith('/')) {
// 内部页面跳转
uni.navigateTo({ url: href })
} else {
// 外部链接,可以复制或用 webview 打开
uni.setClipboardData({
data: href,
success: () => {
uni.showToast({
title: '链接已复制',
icon: 'success'
})
}
})
}
},
onError(err) {
console.error('渲染错误:', err)
// 错误处理逻辑
}
}
}
性能优化建议
1. 图片懒加载
<mp-html :lazy-load="true" />
2. 按需加载
只在需要 Markdown 渲染的页面引入组件,避免全局注册增加包体积。
3. 缓存策略
对于频繁使用的 Markdown 内容,可以考虑缓存解析结果。
4. 样式优化
使用 externStyle 统一配置样式,避免内联样式过多影响性能。
总结
mp-html 确实是一个功能强大的小程序 Markdown 渲染组件,但在使用的过程中需要注意:
- 插件配置:必须正确启用和配置相关插件
- 代码高亮:需要手动扩展 Prism.js 来支持更多语言
-
样式处理:充分利用
externStyle和tag-style进行样式定制 - 事件处理:合理处理图片、链接等交互事件
- 性能优化:开启懒加载,合理使用缓存策略
- Mermaid 图表:需要预处理将 Mermaid 代码转换为图片
虽然踩了不少坑,但最终的效果还是很满意的。现在我们的小程序可以完美地渲染包含代码高亮、表格、图片、Mermaid 图表等多种元素的 Markdown 内容,为用户提供了良好的阅读体验。
特别值得一提的是 Mermaid 图表的处理,这在技术面试题中非常常见。通过预处理将图表转换为图片,我们成功解决了小程序环境中无法直接渲染 Mermaid 的问题。
如果你也在小程序开发中遇到 Markdown 渲染的问题,不妨试试 mp-html 这个组件,希望这篇踩坑记能为你节省一些时间!