Marked.js 的使用及相关问题解决

一、为什么使用Marked.js

marked.js 用于将 markdown 格式字符串转换为对应的带有 html 标签包裹的字符串。通常适用于 AI 产品的聊天窗口,将模型返回的 markdown 格式的字符串转换成对应格式的 html 标签,在聊天窗口中显示。

二、marked.js 的使用方法

1,安装 marked.js

npm i marked / yarn add marked / pnpm install marked

2,使用 marked.js

javascript 复制代码
<template>
    <p v-html="marked(message)"><p/>
<template/>
<script setup>
import { ref} from 'vue'
import {marked} from 'marked'

const message = ref()


<script/>

三、配置换行

因为 marked.js 直接使用是默认不换行的,所以如果想要使 marked.js 的换行效果同后端返回的字符串样式对齐的话,需要对其进行配置 breaks : true ;

javascript 复制代码
marked.setOptions({
  gfm: true,        // 使用 GitHub Flavored Markdown (GFM)
  breaks: true,     // 转换换行符为 <br> 标签
  pedantic: false,  // 不遵守原始的 Markdown 标准
  sanitize: false,  // 不对输出进行清理/转义(默认情况下会转义 HTML)
  smartLists: true, // 使用智能列表识别(例如正确的缩进)
  smartypants: false // 不使用智能引号处理
});

四、重写 p 标签渲染事件, 解决空行丢失问题

如果后端传回的字符串后面带连续的两个\n,在转为 markdown 的时候是需要出现一个空行的,但是 marked.js 会把 \n\n 后面的一段文本转换成 p 标签,所以,空行会消失。所以需要重写 p 标签的渲染事件,paragraph 在 p 标签前面增加一个换行 br 标签

javascript 复制代码
import { marked, Renderer} from 'marked';

// 重写marked 渲染逻辑
const renderer = new Renderer();

marked.setOptions({
  gfm: true,
  smartLists: true,
  renderer: renderer,
  breaks: true,
});

renderer.paragraph = (data) => {
  return "</br>" + "<p>" + marked.parseInline(data.raw) + "</p>";
};

五、重写 li 标签渲染事件, 解决转换有序列表字符串为 li 时,序号丢失问题。

当 marked.js 转换字符串中的有序列表时,会造成序号丢失问题。

例如:1, aaaaaaaaaa。/n 2,bbbbbbbbbb。

marked.js 会将其转换成 <li>aaaaaaaaaa。<li> <li>bbbbbbbbbb。<li/> 。 造成 首位数字的丢失

这里是指 从 1 开始的有序列表转换时会造成 数字的丢失,但是不是从 1 开始的并不会丢失序号数字。

可以通过改写 li 标签的渲染事件 listitem。解决该问题。

javascript 复制代码
// 重写marked 的有序列表,子元素 li 渲染逻辑
renderer.listitem = (data) => {
  // 普通列表项添加自定义 class
  let liTextList;
  liTextList = marked.parseInline(data.raw).split("<br>") as string[];
  // 根据每个字符串中开始的空格缩进来分组用li 包裹
  if (liTextList.length > 1) {
    return liTextList.reduce((prev, curr) => {
      const spaceLength = curr.match(/^\s*/)?.[0].length;
      // 首行有空格,有缩进,并不代表是有 有序列表或者无序列表
      if (spaceLength) {
        // 计算当前字符串的缩进层级
        const level = Math.floor(spaceLength / 2);
        let resultHtmlString = prev + "<li";
        // 此时才是有有序列表或者无序列表的情况
        if (/^ +\* +/.test(curr)) {
          resultHtmlString += ` class='li-level-${level}'>`;
          curr = curr.replace(/^ +\* +/, "");
        } else {
          resultHtmlString += `>`;
        }

        return resultHtmlString + curr + "</li>";
      } else if (/^\* +/.test(curr)) {
        curr = curr.replace(/^\* */, "");
        // 以* 开头,没有缩进,直接是黑色圆点无序列表
        return prev + `<li class="li-level-0">${curr}</li>`;
      } else {
        return prev + `<li>${curr}</li>`;
      }
    }, "");
  } else if (liTextList[0] && /^\* +/.test(liTextList[0])) {
    data.raw = data.raw.replace(/^\* +/, "");
    // 以* 开头,没有缩进,直接是黑色圆点无序列表
    return "<li class='li-level-0'>" + marked.parseInline(data.raw) + "</li>";
  } else {
    // 只有一行的情况下,直接渲染
    return "<li>" + marked.parseInline(data.raw) + "</li>";
  }
};

再根据 level 层级修改对应的 li 标签的样式属性,增加不同的缩进,以及 list-style-type

对于无序列表ul 来说,内层嵌套 li 层级的 list-style-type 的顺序是 disc:黑色小圆点。circle:白色小圆点。square:黑色小方块。依次循环

css 复制代码
  .li-level-0 {
    display: list-item;
    margin-left: 30px;
    list-style-position: outside;
    list-style-type: disc;
  }

  .li-level-2 {
    display: list-item;
    margin-left: 60px;
    list-style-position: outside;
    list-style-type: circle;
  }
  .li-level-4 {
    display: list-item;
    margin-left: 90px;
    list-style-position: outside;
    list-style-type: square;
  }
相关推荐
光影少年4 小时前
react状态管理都有哪些及优缺点和应用场景
前端·react.js·前端框架
saber_andlibert5 小时前
TCMalloc底层实现
java·前端·网络
逍遥德5 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~5 小时前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions5 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子5 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘6 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录6 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
梦帮科技6 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头7 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript