基于 marked.js 的扩展机制,创建一个自定义的块级容器扩展,让内容渲染为<div>标签而非默认的<p>标签

基于 marked.js 的扩展机制,创建一个自定义的块级容器扩展,让内容渲染为

标签而非默认的
标签

概述

marked.js 默认会将 markdown 中的普通文本块渲染为 <p> 标签,本文将介绍如何通过 marked.js 的扩展机制,实现自动将非空文本块渲染为 <div> 标签 ,且遇到空白行时自动分隔生成新的 <div> ,同时保留 markdown 内联语法(如加粗、斜体、链接等)的解析能力,无需手动编写自定义标记(如 ::div)。

实现目标

  1. 普通文本行不再生成 <p> 标签,而是自动生成 <div> 标签;
  2. 连续的非空行合并为一个 <div>
  3. 空白行作为分隔符,空白行后的非空行生成新的 <div>
  4. 保留内联语法(加粗斜体 等)的正常解析;
  5. 不影响 marked.js 其他原生特性(如代码块、列表、标题等)。

实现思路

  1. 扩展类型 :定义 block 级扩展(块级扩展),优先级高于 marked.js 默认的 <p> 标签解析;
  2. 匹配规则:通过正则匹配「连续的非空文本行」,直到空白行或文本结束;
  3. Token 处理:解析匹配到的文本块生成自定义 Token,并处理其中的内联语法 Token;
  4. 渲染逻辑 :将自定义 Token 渲染为 <div> 标签,同时解析内联 Token 保证语法生效。

核心扩展代码

javascript 复制代码
// 1. 定义自定义 div 块级扩展
const autoDivBlock = {
  name: 'autoDiv', // 扩展唯一名称
  level: 'block',  // 块级扩展(覆盖默认<p>标签的块解析)
  /**
   * 标记解析中断点:优先匹配非空行开头,触发当前扩展
   * @param {string} src - 待解析的 markdown 源码
   * @returns {number} 匹配到的位置索引,-1 表示不匹配
   */
  start(src) {
    // 匹配非空、非换行的字符开头(排除空白行)
    const match = src.match(/^[^\n\s]/);
    return match ? match.index : -1;
  },
  /**
   * Token 生成器:解析 markdown 源码,生成自定义 Token
   * @param {string} src - 待解析的 markdown 源码
   * @param {Array} tokens - 已生成的 Token 列表
   * @returns {Object|null} 自定义 Token 对象
   */
  tokenizer(src, tokens) {
    // 正则规则说明:
    // ^           锚定文本开头
    // ([\s\S]*?)  非贪婪匹配所有内容(包括换行),捕获连续非空行
    // (?=\n\n|$)  正向预查:直到空白行(\n\n)或文本结束($)停止
    const rule = /^([\s\S]*?)(?=\n\n|$)/;
    const match = rule.exec(src);

    if (match) {
      // 过滤纯空白的匹配,避免生成空的<div>
      const content = match[1].trim();
      if (!content) return null;

      // 生成自定义 Token
      const token = {
        type: 'autoDiv', // 与扩展 name 对应
        raw: match[0],   // 原始匹配文本(用于消耗源码,避免重复解析)
        text: content,   // 提取 div 内的有效内容
        tokens: []       // 存储内联 Token(如加粗、斜体等)
      };

      // 解析内容中的内联语法,生成子 Token
      this.lexer.inline(token.text, token.tokens);
      return token;
    }
  },
  /**
   * 渲染器:将自定义 Token 转换为 HTML 字符串
   * @param {Object} token - 自定义 Token 对象
   * @returns {string} 渲染后的 HTML 字符串
   */
  renderer(token) {
    // 渲染为<div>,可自定义 class 方便样式控制
    return `<div class="auto-generated-div">${this.parser.parseInline(token.tokens)}</div>`;
  }
};

// 2. 注册扩展并配置 marked.js
marked.use({
  extensions: [autoDivBlock], // 注册自定义扩展
  gfm: true,                  // 保留 GFM 特性(如表格、删除线等)
  breaks: true                // 保留换行符解析
});

// 3. 测试示例
const testMarkdown = `
第一行内容
第二行内容(和第一行同属一个div)

第三行内容(空白行分隔,新div)
**第四行加粗内容**(和第三行同属一个div)

(空白行后无内容,不生成div)
第五行内容(又一个新div)
`;

// 解析 markdown 并输出结果
const resultHtml = marked.parse(testMarkdown);
console.log('渲染后的 HTML:\n', resultHtml);

关键代码解释

1. start 函数

javascript 复制代码
start(src) {
  const match = src.match(/^[^\n\s]/);
  return match ? match.index : -1;
}
  • 作用:告诉 marked.js 「在什么位置停止并检查当前扩展的匹配规则」;
  • 逻辑:匹配「非空、非换行的字符开头」,确保只要有非空行就优先触发当前扩展,覆盖默认的 <p> 标签解析
  • 返回值:匹配到的索引(触发扩展)或 -1(不触发,交给其他扩展/默认解析)。

2. tokenizer 函数

  • 正则规则/^([\s\S]*?)(?=\n\n|$)/ 是核心,实现「捕获连续非空行,空白行分隔」的逻辑:
    • ([\s\S]*?):非贪婪匹配任意字符(包括换行),保证只匹配到下一个分隔符为止;
    • (?=\n\n|$):正向预查,匹配「空白行(\n\n)」或「文本结束($)」,不消耗字符,仅作为分隔标记。
  • Token 处理this.lexer.inline(token.text, token.tokens) 解析文本中的内联语法(如加粗斜体 ),生成子 Token 存储在 token.tokens 中,保证内联语法正常渲染。

3. renderer 函数

javascript 复制代码
renderer(token) {
  return `<div class="auto-generated-div">${this.parser.parseInline(token.tokens)}</div>`;
}
  • 作用:将自定义 Token 转换为最终的 HTML;
  • this.parser.parseInline(token.tokens):将存储的内联子 Token 解析为对应的 HTML(如 **加粗**<strong>加粗</strong>);
  • 自定义 auto-generated-div 类:方便后续通过 CSS 控制 <div> 样式。

测试示例与输出

输入的 Markdown 文本

markdown 复制代码
第一行内容
第二行内容(和第一行同属一个div)

第三行内容(空白行分隔,新div)
**第四行加粗内容**(和第三行同属一个div)

(空白行后无内容,不生成div)
第五行内容(又一个新div)

渲染后的 HTML 输出(格式化后)

html 复制代码
<div class="auto-generated-div">第一行内容
第二行内容(和第一行同属一个div)</div>
<div class="auto-generated-div">第三行内容(空白行分隔,新div)
<strong>第四行加粗内容</strong>(和第三行同属一个div)</div>
<div class="auto-generated-div">第五行内容(又一个新div)</div>

核心特性总结

  1. 自动替换标签 :普通文本块不再生成 <p>,而是自动生成 <div>,无需手动标记;
  2. 空白行分隔 :连续非空行合并为一个 <div>,空白行触发新 <div> 生成;
  3. 保留内联语法:支持加粗、斜体、链接等内联 markdown 语法的正常解析;
  4. 兼容性:不影响 marked.js 原生特性(如代码块、列表、标题等)的解析;
  5. 可扩展性 :可通过修改 renderer 函数自定义 <div> 的 class、style 等属性。

扩展与优化建议

  1. 自定义样式 :通过 auto-generated-div 类为 <div> 添加样式,例如:

    css 复制代码
    .auto-generated-div {
      margin: 8px 0;
      line-height: 1.6;
      padding: 4px 0;
    }
  2. 排除特殊块 :若需要保留代码块、列表等原生块级标签的解析,可在 start 函数中增加排除逻辑;

  3. 自定义分隔规则 :若需修改分隔逻辑(如多个空白行才分隔),可调整 tokenizer 中的正则规则(如 (?=\n{3,}|$) 表示至少3个换行符才分隔)。

相关推荐
悟能不能悟2 小时前
Gson bean getxxx,怎么才能返回给前端
java·前端
2501_944711432 小时前
前端向架构突围系列 - 工程化(五):企业级脚手架的设计与落地
前端·架构
Apex Predator2 小时前
本地库导入到nexus
java·服务器·前端
仍然.2 小时前
Java---反射、枚举、lambda表达式 和 泛型进阶
java·开发语言
趁着年轻吃点苦2 小时前
宝塔面板部署指南
前端
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 21点游戏实现
android·javascript·flutter·游戏·harmonyos
Zsy_0510032 小时前
【C++】类和对象(二)
开发语言·c++
0思必得02 小时前
[Web自动化] Selenium中Select元素操作方法
前端·python·selenium·自动化·html
Duang007_2 小时前
【万字学习总结】API设计与接口开发实战指南
开发语言·javascript·人工智能·python·学习