阅读视图

发现新文章,点击刷新页面。

小程序 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. 代码高亮扩展(关键步骤)

要支持多种编程语言的代码高亮,需要:

  1. 访问 prismjs.com/download.ht…
  2. 选择 Tomorrow Night 主题
  3. 勾选需要的语言:JavaScript, Java, Python, Go, C++, C, HTML, CSS 等
  4. 下载 prism.min.jsprism.css
  5. 替换 node_modules/mp-html/plugins/highlight/ 目录下的对应文件
  6. 重新构建组件: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 `![Mermaid 图表](${imageUrl})`
    } 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 渲染组件,但在使用的过程中需要注意:

  1. 插件配置:必须正确启用和配置相关插件
  2. 代码高亮:需要手动扩展 Prism.js 来支持更多语言
  3. 样式处理:充分利用 externStyletag-style 进行样式定制
  4. 事件处理:合理处理图片、链接等交互事件
  5. 性能优化:开启懒加载,合理使用缓存策略
  6. Mermaid 图表:需要预处理将 Mermaid 代码转换为图片

虽然踩了不少坑,但最终的效果还是很满意的。现在我们的小程序可以完美地渲染包含代码高亮、表格、图片、Mermaid 图表等多种元素的 Markdown 内容,为用户提供了良好的阅读体验。

特别值得一提的是 Mermaid 图表的处理,这在技术面试题中非常常见。通过预处理将图表转换为图片,我们成功解决了小程序环境中无法直接渲染 Mermaid 的问题。

如果你也在小程序开发中遇到 Markdown 渲染的问题,不妨试试 mp-html 这个组件,希望这篇踩坑记能为你节省一些时间!

❌