更新记录

1.0.1(2026-04-17) 下载此版本

优化自定义配置文档,增加图片点击预览示例

1.0.0(2026-02-13) 下载此版本

初始版本,基于Marked.js 17.0.2优化兼容,完整TypeScript支持,支持全平台运行


平台兼容性

uni-app(3.7.6)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟

uni-app x(3.7.6)

Chrome Safari Android iOS 鸿蒙 微信小程序

其他

多语言 暗黑模式 宽屏模式

dh-marked - Markdown解析器(uniapp专用版)

基于Marked.js 17.0.2,专为uniapp优化,兼容全平台(H5、小程序、App)

✨ 特性

  • 全平台兼容 - 支持H5、微信小程序、支付宝小程序、App(iOS/Android)
  • 完整支持 - 支持GitHub风格Markdown(GFM)
  • 轻量快速 - 42KB (gzipped),速度极快
  • TypeScript - 完整的类型定义支持
  • 可扩展 - 支持自定义渲染器和语法扩展
  • 使用方便 - 可以搭配rich-text或者其他富文本显示组件

🚀 快速开始

基础用法

<template>
  <view class="markdown-body">
    <rich-text :nodes="html"></rich-text>
  </view>
</template>

<script>
import { marked } from '@/uni_modules/dh-marked';

export default {
  data() {
    return {
      markdown: '# Hello World\n\n这是**粗体**,这是*斜体*',
    };
  },
  computed: {
    html() {
      return marked.parse(this.markdown);
    },
  },
};
</script>

TypeScript支持

import { marked, type MarkedOptions, type Token } from '@/uni_modules/dh-marked';

// 完整的类型提示
const html: string = marked.parse('# Hello');

// 带配置
const options: MarkedOptions = {
  breaks: true,
  gfm: true,
};
marked.setOptions(options);

📚 核心API

1. marked.parse() - 解析Markdown为HTML

最常用的方法,将Markdown文本转换为HTML。

const html = marked.parse('# 标题\n\n段落内容');
// 输出: <h1>标题</h1>\n<p>段落内容</p>

// 带选项
const html = marked.parse('文本\n换行', {
  breaks: true, // 单个换行转<br>
  gfm: true, // GitHub风格Markdown
});

参数:

  • src: string - Markdown源文本
  • options?: MarkedOptions - 可选配置

返回: string | Promise<string> - HTML字符串


2. marked.parseInline() - 只解析内联元素

只解析加粗、斜体、链接等内联元素,不解析标题、列表等块级元素。

const html = marked.parseInline('**粗体** _斜体_ [链接](url)');
// 输出: <strong>粗体</strong> <em>斜体</em> <a href="url">链接</a>

// 不会解析块级元素
marked.parseInline('# 标题\n\n段落');
// 输出: # 标题\n\n段落 (保持原样)

使用场景: 单行文本、评论预览、标题文字


3. marked.setOptions() - 全局配置

设置全局默认配置,影响后续所有的parse调用。

marked.setOptions({
  breaks: true, // 单个换行转<br>(默认false)
  gfm: true, // GitHub风格Markdown(默认true)
  pedantic: false, // 严格遵循markdown.pl(默认false)
  silent: false, // 安静模式,遇错不抛异常(默认false)
});

常用配置项:

配置项 类型 默认值 说明
breaks boolean false 单个换行是否转<br>
gfm boolean true GitHub风格Markdown
pedantic boolean false 严格模式
silent boolean false 安静模式
renderer Renderer null 自定义渲染器
tokenizer Tokenizer null 自定义分词器
walkTokens function null token遍历钩子

4. marked.use() - 扩展和自定义

扩展marked的功能,自定义渲染规则或添加新语法。

自定义渲染

// 自定义标题渲染
marked.use({
  renderer: {
    heading(token) {
      const level = token.depth;
      const text = this.parser.parseInline(token.tokens);
      return `<h${level} class="my-heading" id="${token.text}">${text}</h${level}>\n`;
    },

    // 自定义链接渲染(用 this.parser.parseInline 保留嵌套格式)
    link(token) {
      const text = this.parser.parseInline(token.tokens);
      return `<a href="${token.href}" class="external-link" target="_blank">${text}</a>`;
    },

    // 自定义图片渲染(rich-text 只支持标准 HTML 标签,用 @itemclick 处理点击,见示例2)
    image(token) {
      return `<img src="${token.href}" alt="${token.text || ''}" style="max-width:100%;border-radius:8px;" />`;
    },
  },
});

添加自定义语法

// 添加警告框语法:!!! 警告内容
marked.use({
  extensions: [
    {
      name: 'alert',
      level: 'block',
      tokenizer(src) {
        const match = /^!!! (.+)\n/.exec(src);
        if (match) {
          return {
            type: 'alert',
            raw: match[0],
            text: match[1],
          };
        }
      },
      renderer(token) {
        // rich-text 只支持标准 HTML 标签,不能用 <view>
        return `<div class="alert">${token.text}</div>\n`;
      },
    },
  ],
});

// 使用
const html = marked.parse('!!! 这是警告信息');
// 输出: <div class="alert">这是警告信息</div>

5. marked.lexer() - 词法分析

将Markdown文本转换为token数组(语法树)。

const tokens = marked.lexer('# 标题\n\n段落内容');
console.log(tokens);
/*
[
  {
    type: 'heading',
    depth: 1,
    text: '标题',
    tokens: [...]
  },
  {
    type: 'paragraph',
    text: '段落内容',
    tokens: [...]
  }
]
*/

使用场景: 需要分析或修改Markdown结构


6. marked.parser() - 将tokens渲染为HTML

将token数组渲染为HTML字符串。

const tokens = marked.lexer('# Hello');
const html = marked.parser(tokens);
// 输出: <h1>Hello</h1>

// 可以先修改tokens再渲染
tokens[0].text = '修改后的标题';
const newHtml = marked.parser(tokens);

使用场景: 需要在渲染前修改语法树


7. marked.walkTokens() - 遍历所有token

遍历语法树中的所有token,可用于分析或修改内容。

const tokens = marked.lexer('# 标题\n\n[链接](url)');

marked.walkTokens(tokens, (token) => {
  // 修改所有链接
  if (token.type === 'link') {
    token.href = 'https://new-domain.com' + token.href;
  }

  // 收集所有标题
  if (token.type === 'heading') {
    console.log(`H${token.depth}: ${token.text}`);
  }
});

const html = marked.parser(tokens);

🎨 实战示例

示例1:Markdown文章渲染器

<template>
  <scroll-view class="article-container">
    <view class="markdown-body">
      <rich-text :nodes="html"></rich-text>
    </view>
  </scroll-view>
</template>

<script>
import { marked } from '@/uni_modules/dh-marked';

export default {
  data() {
    return {
      markdown: '',
    };
  },
  computed: {
    html() {
      return marked.parse(this.markdown || '');
    },
  },
  onLoad(options) {
    // 配置marked
    marked.setOptions({
      breaks: true, // 换行转<br>
      gfm: true, // GitHub风格
    });

    // 加载文章
    this.loadArticle(options.id);
  },
  methods: {
    loadArticle(id) {
      uni.request({
        url: '/api/article/' + id,
        success: (res) => {
          this.markdown = res.data.content;
        },
      });
    },
  },
};
</script>

<style>
/* GitHub样式的Markdown */
.markdown-body h1 {
  font-size: 32px;
  font-weight: bold;
  margin: 20px 0;
}
.markdown-body h2 {
  font-size: 28px;
  font-weight: bold;
  margin: 18px 0;
}
.markdown-body p {
  line-height: 1.6;
  margin: 10px 0;
}
.markdown-body code {
  background: #f5f5f5;
  padding: 2px 4px;
  border-radius: 3px;
}
.markdown-body pre {
  background: #f5f5f5;
  padding: 10px;
  border-radius: 5px;
  overflow: auto;
}
</style>

示例2:图片点击预览(rich-text @itemclick)

rich-text 组件通过 @itemclick 事件监听内部元素点击,通过 e.detail.node 判断点击的节点类型。

<template>
  <view class="markdown-body">
    <!-- 绑定 @itemclick,而不是 @tap -->
    <rich-text :nodes="html" @itemclick="onImageItemClick"></rich-text>
  </view>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { marked } from '@/uni_modules/dh-marked';

// 自定义图片渲染器(可选,保持原始 img 标签即可)
marked.use({
  renderer: {
    image(token) {
      return `<img src="${token.href}" alt="${token.text || ''}" style="max-width:100%;" />`;
    },
  },
});

const markdown = `# 图片示例\n\n![图片描述](https://example.com/image.jpg)`;

const html = computed(() => marked.parse(markdown));

// 处理 rich-text 内部元素点击
function onImageItemClick(e: any) {
  const node = e.detail?.node;
  if (node?.name === 'img') {
    const src = node.attrs?.src as string;
    if (src) {
      uni.previewImage({
        urls: [src],
        current: src,
      });
    }
  }
}
</script>

注意:

  • @itemclick 是 rich-text 专用的点击事件,e.detail.node.name 为 HTML 标签名(如 'img''a'
  • e.detail.node.attrs 包含该节点的所有属性(如 srchref
  • 不要在 HTML 字符串中使用 @tap 等事件属性,rich-text 不支持内联事件

示例3:生成文章目录

import { marked } from '@/uni_modules/dh-marked';

function generateTOC(markdown) {
  const tokens = marked.lexer(markdown);
  const toc = [];

  marked.walkTokens(tokens, (token) => {
    if (token.type === 'heading') {
      toc.push({
        level: token.depth,
        text: token.text,
        id: token.text.toLowerCase().replace(/\s+/g, '-'),
      });
    }
  });

  return toc;
}

// 使用
const markdown = '# 一级标题\n## 二级标题\n### 三级标题';
const toc = generateTOC(markdown);
/*
[
  { level: 1, text: '一级标题', id: '一级标题' },
  { level: 2, text: '二级标题', id: '二级标题' },
  { level: 3, text: '三级标题', id: '三级标题' }
]
*/

示例4:提取纯文本摘要

import { marked } from '@/uni_modules/dh-marked';

function getPlainText(markdown, maxLength = 100) {
  const tokens = marked.lexer(markdown);
  let text = '';

  marked.walkTokens(tokens, (token) => {
    if (token.type === 'text') {
      text += token.text + ' ';
    }
  });

  const trimmed = text.trim();
  return trimmed.length > maxLength ? trimmed.slice(0, maxLength) + '...' : trimmed;
}

// 使用
const summary = getPlainText('# 标题\n\n这是一段**很长**的内容...');
// 输出: "标题 这是一段很长的内容..."

示例5:代码高亮(使用highlight.js)

import { marked } from '@/uni_modules/dh-marked';
import hljs from 'highlight.js'; // 需要另外引入

marked.use({
  renderer: {
    code(token) {
      const language = token.lang || 'plaintext';
      const code = token.text;

      // 使用highlight.js高亮
      const highlighted = hljs.highlight(code, { language }).value;

      return `<pre><code class="language-${language}">${highlighted}</code></pre>\n`;
    },
  },
});

🔧 高级用法

创建独立实例

import { Marked } from '@/uni_modules/dh-marked';

// 创建独立实例,不影响全局配置
const customMarked = new Marked({
  breaks: true,
  gfm: false,
});

const html1 = customMarked.parse('文本\n换行'); // 有<br>
const html2 = marked.parse('文本\n换行'); // 无<br>

使用Hooks钩子

import { marked } from '@/uni_modules/dh-marked';

marked.use({
  hooks: {
    // 预处理Markdown
    preprocess(markdown) {
      // 替换emoji短代码
      return markdown.replace(/:smile:/g, '😊').replace(/:heart:/g, '❤️');
    },

    // 后处理HTML
    postprocess(html) {
      // 为所有图片添加懒加载
      return html.replace(/<img/g, '<img loading="lazy"');
    },
  },
});

📖 完整API列表

基础API

API 用途 常用度
marked.parse() 解析Markdown为HTML ⭐⭐⭐⭐⭐
marked.parseInline() 只解析内联元素 ⭐⭐⭐
marked.setOptions() 全局配置 ⭐⭐⭐⭐
marked.use() 扩展/自定义 ⭐⭐⭐⭐
marked.getDefaults() 获取默认配置 ⭐⭐

高级API

API 用途 常用度
marked.lexer() 词法分析 ⭐⭐⭐
marked.parser() tokens转HTML ⭐⭐⭐
marked.walkTokens() 遍历tokens ⭐⭐⭐

类API

用途 常用度
Marked 独立实例 ⭐⭐
Renderer 渲染器类 ⭐⭐⭐
Tokenizer 分词器类 ⭐⭐
Lexer 词法分析器类 ⭐⭐
Parser 解析器类 ⭐⭐
TextRenderer 纯文本渲染 ⭐⭐
Hooks 钩子系统 ⭐⭐⭐

⚙️ 配置选项详解

MarkedOptions

interface MarkedOptions {
  // 单个换行是否转<br>
  breaks?: boolean; // 默认: false

  // GitHub风格Markdown
  gfm?: boolean; // 默认: true

  // 严格模式
  pedantic?: boolean; // 默认: false

  // 安静模式(错误不抛异常)
  silent?: boolean; // 默认: false

  // 异步解析
  async?: boolean; // 默认: false

  // 自定义渲染器
  renderer?: Renderer;

  // 自定义分词器
  tokenizer?: Tokenizer;

  // 钩子函数
  hooks?: Hooks;

  // 扩展语法
  extensions?: Extension[];

  // token遍历函数
  walkTokens?: (token: Token) => void;
}

📄 许可证

MIT License

基于 Marked.js - Copyright (c) 2018+, MarkedJS (MIT License)


🔗 相关链接


💡 常见问题

Q: 如何在rich-text中使用?

A: 直接将解析后的HTML传给rich-text组件:

<rich-text :nodes="marked.parse(markdown)"></rich-text>

Q: 图片如何支持预览?

A: 在 rich-text 上绑定 @itemclick,在回调中判断 e.detail.node.name === 'img' 后调用 uni.previewImage,参考示例2。

Q: 支持代码高亮吗?

A: 需要配合highlight.js使用,参考示例5。

Q: 如何生成目录?

A: 使用marked.walkTokens()遍历heading类型的token,参考示例3。

Q: 性能如何?

A: Marked是最快的Markdown解析器之一,10KB的文档解析时间<5ms。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议