告别单调文本:markdown-it 自定义容器完全指南

告别单调文本:markdown-it 自定义容器完全指南

【免费下载链接】markdown-it Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed 【免费下载链接】markdown-it 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-it

你是否还在为 Markdown 文档中无法突出显示重要提示而烦恼?是否需要在技术文档中添加醒目的警告框或信息块?本文将带你深入了解如何使用 markdown-it 开发自定义容器,通过 step-by-step 实现可定制的提示框与警告块语法,让你的文档更具专业性和可读性。

读完本文你将掌握:

  • 自定义容器的工作原理与实现流程
  • 完整的提示框/警告框组件开发代码
  • 容器样式美化与高级功能扩展
  • 在实际项目中集成与使用自定义容器

自定义容器开发基础

工作原理概述

markdown-it 作为一款高度可扩展的 Markdown 解析器(Parser),其核心架构采用了插件化设计。自定义容器本质上是通过扩展其块级解析规则(Block Rule)和渲染器(Renderer)实现的语法扩展。

mermaid

整个流程包含三个关键步骤:

  1. 规则注册:向解析器添加新的块级识别规则
  2. Token 生成:将自定义语法转换为抽象语法树节点
  3. HTML 渲染:定义 Token 到 HTML 的转换规则

开发环境准备

首先确保已安装必要的依赖:

# 克隆仓库
git clone https://gitcode.com/gh_mirrors/ma/markdown-it
cd markdown-it

# 安装依赖
npm install

创建自定义容器开发的基础文件结构:

mkdir -p demo/custom-container
touch demo/custom-container/index.js
touch demo/custom-container/style.css
touch demo/custom-container/example.md

实现自定义容器核心代码

1. 块级解析规则开发

创建 custom-container.js 文件,实现容器识别逻辑:

export default function customContainerPlugin(md, options) {
  // 默认配置
  const defaultOptions = {
    types: ['tip', 'warning', 'danger'],
    defaultTitle: {
      tip: '提示',
      warning: '警告',
      danger: '危险'
    }
  };
  
  const opts = { ...defaultOptions, ...options };
  
  // 注册块级规则
  md.block.ruler.before('fence', 'custom_container', (state, startLine, endLine, silent) => {
    let start = state.bMarks[startLine] + state.tShift[startLine];
    let end = state.eMarks[startLine];
    const marker = state.src.charCodeAt(start);
    
    // 检查是否以 ::: 开头
    if (marker !== 0x3A /* : */) return false;
    
    // 检查是否有三个连续的冒号
    let match = 0;
    let pos = start;
    while (pos < end && state.src.charCodeAt(pos) === marker) {
      match++;
      pos++;
    }
    if (match < 3) return false;
    
    // 提取容器类型和标题
    const content = state.src.slice(pos, end).trim();
    const typeMatch = content.match(/^(\w+)(?:\s+(.*))?$/);
    if (!typeMatch || !opts.types.includes(typeMatch[1])) return false;
    
    const type = typeMatch[1];
    const title = typeMatch[2] || opts.defaultTitle[type];
    
    // 静默模式下返回 true
    if (silent) return true;
    
    // 查找结束行
    let nextLine = startLine;
    let haveEndMarker = false;
    for (;;) {
      nextLine++;
      if (nextLine >= endLine) break;
      
      start = state.bMarks[nextLine] + state.tShift[nextLine];
      end = state.eMarks[nextLine];
      
      if (start >= end) { nextLine++; continue; }
      
      // 检查结束标记
      match = 0;
      pos = start;
      while (pos < end && state.src.charCodeAt(pos) === marker) {
        match++;
        pos++;
      }
      if (match >= 3 && state.src.slice(pos, end).trim() === '') {
        haveEndMarker = true;
        break;
      }
    }
    
    // 更新解析状态
    state.line = haveEndMarker ? nextLine + 1 : endLine;
    
    // 创建容器开始 Token
    const tokenOpen = state.push(`custom_container_${type}_open`, 'div', 1);
    tokenOpen.attrs = [
      ['class', `custom-container custom-container-${type}`],
      ['data-title', title]
    ];
    tokenOpen.map = [startLine, state.line];
    
    // 处理容器内容
    state.md.block.tokenize(state, startLine + 1, nextLine);
    
    // 创建容器结束 Token
    const tokenClose = state.push(`custom_container_${type}_close`, 'div', -1);
    tokenClose.map = [startLine, state.line];
    
    return true;
  });
}

2. 注册渲染规则

在同文件中添加渲染器规则:

// ... 继续上面的 customContainerPlugin 函数

// 注册渲染规则
opts.types.forEach(type => {
  md.renderer.rules[`custom_container_${type}_open`] = (tokens, idx) => {
    const token = tokens[idx];
    const title = token.attrGet('data-title');
    return `<div class="${token.attrGet('class')}">
              <div class="custom-container-title">${title}</div>
              <div class="custom-container-content">
           `;
  };
  
  md.renderer.rules[`custom_container_${type}_close`] = () => `
              </div>
            </div>
          `;
});

3. 完整插件实现

整合上述代码,形成完整插件:

export default function customContainerPlugin(md, options) {
  // 默认配置
  const defaultOptions = {
    types: ['tip', 'warning', 'danger'],
    defaultTitle: {
      tip: '提示',
      warning: '警告',
      danger: '危险'
    }
  };
  
  const opts = { ...defaultOptions, ...options };
  
  // 注册块级规则
  md.block.ruler.before('fence', 'custom_container', (state, startLine, endLine, silent) => {
    // ... 块级解析代码 ...
  });
  
  // 注册渲染规则
  opts.types.forEach(type => {
    // ... 渲染规则代码 ...
  });
  
  return md;
}

样式美化与应用

CSS 样式设计

创建 custom-container.css 文件,添加容器样式:

.custom-container {
  margin: 1rem 0;
  padding: 0 1rem;
  border-radius: 4px;
  overflow: hidden;
}

.custom-container-title {
  font-weight: bold;
  padding: 0.75rem 0;
  margin: 0 -1rem;
  padding-left: 1rem;
  border-bottom: 1px solid rgba(0,0,0,0.1);
}

.custom-container-content {
  padding: 1rem 0;
}

/* 提示类型容器 */
.custom-container-tip {
  border-left: 4px solid #4CAF50;
  background-color: #f8fff8;
}

.custom-container-tip .custom-container-title {
  background-color: #f0f9f0;
  color: #2e7d32;
}

/* 警告类型容器 */
.custom-container-warning {
  border-left: 4px solid #FFC107;
  background-color: #fffcf5;
}

.custom-container-warning .custom-container-title {
  background-color: #fff8e1;
  color: #ff8f00;
}

/* 危险类型容器 */
.custom-container-danger {
  border-left: 4px solid #F44336;
  background-color: #fff8f8;
}

.custom-container-danger .custom-container-title {
  background-color: #ffebee;
  color: #c62828;
}

使用示例与效果展示

创建 example.md 文件:

# 自定义容器示例

## 提示容器

::: tip
这是一个提示容器,用于展示有用的额外信息。
- 支持列表
- **支持 Markdown 格式**
- [链接示例](#)
:::

## 警告容器

::: warning
这是一个警告容器,用于展示需要注意的内容。
> 可以包含引用块
```javascript
// 也支持代码块
function warning() {
  console.log('注意!');
}

:::

危险容器

::: danger 安全警告 这是一个危险容器,用于展示重要的安全提示。 :::


### 在项目中集成

创建 `demo.js` 文件,演示如何在项目中使用:

```javascript
import MarkdownIt from 'markdown-it';
import customContainer from './custom-container.js';
import fs from 'fs';
import path from 'path';

// 初始化 markdown-it 实例
const md = new MarkdownIt({
  html: true,
  linkify: true,
  typographer: true
}).use(customContainer, {
  // 自定义配置
  types: ['tip', 'warning', 'danger', 'info'],
  defaultTitle: {
    tip: '提示',
    warning: '警告',
    danger: '危险',
    info: '信息'
  }
});

// 读取示例 Markdown 文件
const examplePath = path.join(process.cwd(), 'example.md');
const markdownContent = fs.readFileSync(examplePath, 'utf-8');

// 渲染为 HTML
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>markdown-it 自定义容器示例</title>
  <link rel="stylesheet" href="custom-container.css">
</head>
<body>
  <div class="markdown-body">
    ${md.render(markdownContent)}
  </div>
</body>
</html>
`;

// 输出 HTML 文件
fs.writeFileSync('demo.html', htmlContent);
console.log('示例已生成: demo.html');

运行演示代码:

node demo.js

打开生成的 demo.html 文件,即可看到渲染效果。

高级功能扩展

自定义图标支持

通过添加图标字体(如 Font Awesome)增强视觉效果:

/* 在 custom-container.css 中添加 */
.custom-container-title::before {
  font-family: "Font Awesome 6 Free";
  font-weight: 900;
  margin-right: 0.5rem;
  content: "\f05a"; /* 默认图标 */
}

.custom-container-tip .custom-container-title::before {
  content: "\f058"; /* 对勾图标 */
}

.custom-container-warning .custom-container-title::before {
  content: "\f071"; /* 感叹号图标 */
}

.custom-container-danger .custom-container-title::before {
  content: "\f06a"; /* 警告图标 */
}

在 HTML 中引入 Font Awesome:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">

折叠功能实现

添加折叠/展开功能:

// 修改渲染规则
md.renderer.rules[`custom_container_${type}_open`] = (tokens, idx) => {
  const token = tokens[idx];
  const title = token.attrGet('data-title');
  const containerId = `container-${type}-${Math.random().toString(36).substr(2, 9)}`;
  
  return `<div class="${token.attrGet('class')}">
            <div class="custom-container-title" onclick="toggleContainer('${containerId}')">
              <i class="fa fa-chevron-down toggle-icon"></i> ${title}
            </div>
            <div id="${containerId}" class="custom-container-content">
         `;
};

// 添加折叠控制函数
// 在 HTML 中添加
<script>
function toggleContainer(id) {
  const content = document.getElementById(id);
  const icon = content.previousElementSibling.querySelector('.toggle-icon');
  
  if (content.style.display === 'none') {
    content.style.display = 'block';
    icon.classList.remove('fa-chevron-right');
    icon.classList.add('fa-chevron-down');
  } else {
    content.style.display = 'none';
    icon.classList.remove('fa-chevron-down');
    icon.classList.add('fa-chevron-right');
  }
}
</script>

自定义容器嵌套支持

修改块级解析规则,支持容器嵌套:

// 修改规则注册位置,确保在列表规则之后
md.block.ruler.after('list', 'custom_container', /* ... */);

// 调整状态检查
if (state.sCount[startLine] - state.blkIndent >= 4) {
  // 允许缩进,但不超过 3 个空格
  return false;
}

插件发布与维护

打包与发布

创建 package.json

{
  "name": "markdown-it-custom-container",
  "version": "1.0.0",
  "description": "Custom container plugin for markdown-it",
  "main": "custom-container.js",
  "keywords": ["markdown-it", "markdown-it-plugin", "custom-container"],
  "author": "",
  "license": "MIT",
  "peerDependencies": {
    "markdown-it": ">=10.0.0"
  }
}

发布到 npm:

npm login
npm publish

性能优化建议

  1. 规则顺序优化:将自定义容器规则放在合适的位置,避免不必要的解析尝试
// 对于简单容器,可放在 paragraph 规则之前
md.block.ruler.before('paragraph', 'custom_container', /* ... */);

// 对于复杂容器,可放在 fence 规则之后
md.block.ruler.after('fence', 'custom_container', /* ... */);
  1. 使用静默模式快速排除:在规则函数开始处添加快速检查
// 快速检查是否可能是自定义容器
if (state.src.charCodeAt(start) !== 0x3A /* : */) return false;
if (state.src.charCodeAt(start + 1) !== 0x3A /* : */) return false;
if (state.src.charCodeAt(start + 2) !== 0x3A /* : */) return false;
  1. 限制最大嵌套深度:防止过深嵌套导致性能问题
if (state.level > 10) return false; // 限制最大嵌套深度

总结与最佳实践

开发流程回顾

mermaid

常见问题解决方案

  1. 与其他规则冲突
// 调整规则顺序
md.block.ruler.before('fence', 'custom_container', /* ... */);
// 或
md.block.ruler.after('fence', 'custom_container', /* ... */);
  1. Markdown 内容渲染问题
// 确保正确处理内容
state.md.block.tokenize(state, startLine + 1, nextLine);
  1. 嵌套容器样式异常
/* 添加嵌套样式支持 */
.custom-container .custom-container {
  margin: 1rem 0;
  border-radius: 4px;
}

未来扩展方向

  1. 自定义主题支持:允许用户通过配置自定义容器样式
  2. 动态内容加载:支持从外部数据源加载容器内容
  3. 交互功能增强:添加标签页、评分等复杂交互组件
  4. 导出功能:支持将容器内容导出为图片或PDF

通过本文介绍的方法,你已经掌握了 markdown-it 自定义容器的开发技巧。现在就可以将这一功能应用到你的项目中,创建更加丰富和专业的 Markdown 文档。无论是技术文档、博客文章还是知识库系统,自定义容器都能帮助你更好地组织和展示内容,提升用户体验。

【免费下载链接】markdown-it Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed 【免费下载链接】markdown-it 项目地址: https://gitcode.com/gh_mirrors/ma/markdown-it

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值