Skip to content

Markdown 插件生态系统

Markdown 的真正威力在于其可扩展性。通过插件,你可以添加新功能、自定义语法,并将 Markdown 与各种工具和服务集成。

流行的 Markdown 处理器

Marked.js

快速的 JavaScript Markdown 解析器。

安装

bash
npm install marked

基本使用

javascript
const marked = require('marked');

const markdown = '# Hello World';
const html = marked.parse(markdown);

扩展 Marked

javascript
const marked = require('marked');

// 自定义渲染器
const renderer = {
  heading(text, level) {
    return `<h${level} class="custom-heading">${text}</h${level}>`;
  },
  link(href, title, text) {
    return `<a href="${href}" target="_blank">${text}</a>`;
  }
};

marked.use({ renderer });

Markdown-it

可插拔的 Markdown 解析器,速度快且易于扩展。

安装

bash
npm install markdown-it

基本使用

javascript
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

const result = md.render('# Hello World');

使用插件

javascript
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

// 添加插件
md.use(require('markdown-it-emoji'));
md.use(require('markdown-it-footnote'));
md.use(require('markdown-it-anchor'));
md.use(require('markdown-it-table-of-contents'));

const result = md.render('# 标题 :smile:');

Remark

由插件驱动的 Markdown 处理器,基于统一的生态系统。

安装

bash
npm install remark remark-html

基本使用

javascript
const remark = require('remark');
const html = require('remark-html');

remark()
  .use(html)
  .process('# Hello World', (err, file) => {
    console.log(String(file));
  });

插件链

javascript
const remark = require('remark');
const remarkGfm = require('remark-gfm');
const remarkToc = require('remark-toc');
const remarkHtml = require('remark-html');

remark()
  .use(remarkGfm)
  .use(remarkToc)
  .use(remarkHtml)
  .process(markdown);

必备插件类别

1. 语法扩展插件

GitHub Flavored Markdown (GFM)

bash
npm install remark-gfm
javascript
const remark = require('remark');
const gfm = require('remark-gfm');

remark()
  .use(gfm)
  .process('~~删除线~~ 和任务列表 [x]');

表情符号

bash
npm install markdown-it-emoji
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-emoji'));

md.render('你好 :smile:');  // 你好 😄

脚注

bash
npm install markdown-it-footnote
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-footnote'));

const markdown = `
这是文本[^1]。

[^1]: 这是脚注。
`;
md.render(markdown);

定义列表

bash
npm install markdown-it-deflist
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-deflist'));

const markdown = `
术语
: 定义
`;
md.render(markdown);

2. 目录 (TOC) 插件

Remark TOC

bash
npm install remark-toc
javascript
const remark = require('remark');
const toc = require('remark-toc');

remark()
  .use(toc, { heading: '目录' })
  .process(markdown);

Markdown-it TOC

bash
npm install markdown-it-table-of-contents
npm install markdown-it-anchor
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-anchor'));
md.use(require('markdown-it-table-of-contents'), {
  includeLevel: [1, 2, 3]
});

const markdown = `
[[toc]]

# 标题 1
## 标题 2
`;
md.render(markdown);

3. 代码高亮插件

Highlight.js

bash
npm install highlight.js
javascript
const marked = require('marked');
const hljs = require('highlight.js');

marked.setOptions({
  highlight: function(code, lang) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(code, { language: lang }).value;
    }
    return code;
  }
});

Prism.js

bash
npm install prismjs
javascript
const Prism = require('prismjs');
require('prismjs/components/prism-python');

const code = `def hello():
    print("Hello")`;

const html = Prism.highlight(code, Prism.languages.python, 'python');

Shiki

bash
npm install shiki
javascript
const shiki = require('shiki');

shiki.getHighlighter({
  theme: 'nord'
}).then(highlighter => {
  const html = highlighter.codeToHtml('const a = 1', { lang: 'js' });
  console.log(html);
});

4. 数学公式插件

KaTeX

bash
npm install markdown-it-katex
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-katex'));

const markdown = `
$$
E = mc^2
$$

行内公式 $x^2 + y^2 = z^2$
`;
md.render(markdown);

MathJax

bash
npm install markdown-it-mathjax3
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-mathjax3'));

const markdown = `
$$
\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}
$$
`;
md.render(markdown);

5. 图表插件

Mermaid

bash
npm install markdown-it-mermaid
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-mermaid'));

const markdown = `
```mermaid
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
```
`;
md.render(markdown);

Chart.js

bash
npm install remark-chart

6. 容器插件

自定义容器

bash
npm install markdown-it-container
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-container'), 'warning', {
  validate: params => params.trim() === 'warning',
  render: (tokens, idx) => {
    if (tokens[idx].nesting === 1) {
      return '<div class="warning">\n';
    } else {
      return '</div>\n';
    }
  }
});

const markdown = `
::: warning
这是一个警告
:::
`;
md.render(markdown);

7. 链接处理插件

外部链接

bash
npm install markdown-it-link-attributes
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-link-attributes'), {
  pattern: /^https?:/,
  attrs: {
    target: '_blank',
    rel: 'noopener'
  }
});

链接检查

bash
npm install remark-lint-no-dead-urls
javascript
const remark = require('remark');
const lint = require('remark-lint-no-dead-urls');

remark()
  .use(lint)
  .process(markdown);

8. 图片处理插件

图片懒加载

bash
npm install markdown-it-lazy-loading
javascript
const md = require('markdown-it')();
md.use(require('markdown-it-lazy-loading'));

响应式图片

bash
npm install remark-images
javascript
const remark = require('remark');
const images = require('remark-images');

remark()
  .use(images, {
    figureClassName: 'responsive-image'
  })
  .process(markdown);

创建自定义插件

Markdown-it 插件

简单插件

javascript
function myPlugin(md) {
  // 保存原始的段落渲染规则
  const defaultRender = md.renderer.rules.paragraph_open || 
    function(tokens, idx, options, env, self) {
      return self.renderToken(tokens, idx, options);
    };

  // 覆盖段落渲染
  md.renderer.rules.paragraph_open = function(tokens, idx, options, env, self) {
    tokens[idx].attrSet('class', 'custom-paragraph');
    return defaultRender(tokens, idx, options, env, self);
  };
}

const md = require('markdown-it')();
md.use(myPlugin);

带选项的插件

javascript
function highlightPlugin(md, options) {
  const defaultOptions = {
    className: 'highlight',
    marker: '=='
  };
  
  const opts = Object.assign({}, defaultOptions, options);
  
  md.inline.ruler.before('emphasis', 'highlight', function(state, silent) {
    // 插件逻辑
    const start = state.pos;
    const marker = state.src.slice(start, start + 2);
    
    if (marker !== opts.marker) {
      return false;
    }
    
    // 查找结束标记
    let found = false;
    let end = start + 2;
    
    while (end < state.posMax) {
      if (state.src.slice(end, end + 2) === opts.marker) {
        found = true;
        break;
      }
      end++;
    }
    
    if (!found) {
      return false;
    }
    
    if (!silent) {
      const token = state.push('highlight_open', 'mark', 1);
      token.attrSet('class', opts.className);
      
      const text = state.push('text', '', 0);
      text.content = state.src.slice(start + 2, end);
      
      state.push('highlight_close', 'mark', -1);
    }
    
    state.pos = end + 2;
    return true;
  });
}

const md = require('markdown-it')();
md.use(highlightPlugin, { className: 'my-highlight' });

md.render('这是==高亮==文本');  // <mark class="my-highlight">高亮</mark>

Remark 插件

简单转换插件

javascript
const visit = require('unist-util-visit');

function remarkPlugin() {
  return function transformer(tree) {
    visit(tree, 'text', function(node) {
      // 将文本转为大写
      node.value = node.value.toUpperCase();
    });
  };
}

const remark = require('remark');
remark()
  .use(remarkPlugin)
  .process('hello world');

高级插件

javascript
const visit = require('unist-util-visit');

function customCallout() {
  return function transformer(tree) {
    visit(tree, 'blockquote', function(node, index, parent) {
      // 检查是否是特殊的 callout 语法
      if (node.children[0] && 
          node.children[0].type === 'paragraph') {
        const firstChild = node.children[0].children[0];
        
        if (firstChild && firstChild.value) {
          const match = firstChild.value.match(/^\[!(\w+)\]/);
          
          if (match) {
            const type = match[1].toLowerCase();
            
            // 移除标记
            firstChild.value = firstChild.value.replace(/^\[!\w+\]\s*/, '');
            
            // 转换为自定义节点
            const callout = {
              type: 'callout',
              data: {
                hName: 'div',
                hProperties: {
                  className: ['callout', `callout-${type}`]
                }
              },
              children: node.children
            };
            
            parent.children[index] = callout;
          }
        }
      }
    });
  };
}

const remark = require('remark');
remark()
  .use(customCallout)
  .process('> [!NOTE]\n> 这是一个提示');

插件开发最佳实践

1. 命名规范

  • Markdown-it: markdown-it-{name}
  • Remark: remark-{name}
  • Rehype: rehype-{name}

2. 选项处理

javascript
function myPlugin(md, options) {
  const defaults = {
    className: 'default',
    enabled: true
  };
  
  const opts = Object.assign({}, defaults, options);
  
  if (!opts.enabled) {
    return;
  }
  
  // 插件逻辑
}

3. 错误处理

javascript
function safePlugin(md) {
  try {
    // 插件逻辑
  } catch (error) {
    console.error('插件错误:', error);
    // 不要中断整个处理流程
  }
}

4. 性能优化

javascript
function optimizedPlugin(md) {
  // 缓存常用值
  const cache = new Map();
  
  md.core.ruler.push('optimize', function(state) {
    // 只在必要时处理
    if (cache.has(state.src)) {
      return cache.get(state.src);
    }
    
    // 处理逻辑
    const result = process(state);
    cache.set(state.src, result);
    return result;
  });
}

5. 测试

javascript
const assert = require('assert');
const md = require('markdown-it')();
md.use(myPlugin);

describe('MyPlugin', () => {
  it('should transform correctly', () => {
    const input = '测试输入';
    const expected = '<p>预期输出</p>\n';
    const result = md.render(input);
    assert.strictEqual(result, expected);
  });
});

插件集合推荐

Markdown-it 插件包

bash
npm install markdown-it-plugins-pack

包含:

  • markdown-it-emoji
  • markdown-it-footnote
  • markdown-it-sub
  • markdown-it-sup
  • markdown-it-mark
  • markdown-it-abbr
  • markdown-it-container

Remark 预设

bash
npm install remark-preset-lint-recommended

包含常用的 lint 规则和插件。

调试插件

打印 AST

javascript
const remark = require('remark');
const inspect = require('unist-util-inspect');

remark()
  .use(() => tree => {
    console.log(inspect(tree));
  })
  .process(markdown);

调试 Markdown-it

javascript
const md = require('markdown-it')();

// 打印 tokens
const tokens = md.parse(markdown, {});
console.log(JSON.stringify(tokens, null, 2));

// 打印渲染结果
console.log(md.render(markdown));

结论

Markdown 插件生态系统非常丰富,几乎可以满足任何需求。选择合适的处理器和插件,可以大大增强 Markdown 的功能。如果找不到合适的插件,创建自定义插件也相对简单。

其他资源

由 Markdownlang.com 整理创建