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-gfmjavascript
const remark = require('remark');
const gfm = require('remark-gfm');
remark()
.use(gfm)
.process('~~删除线~~ 和任务列表 [x]');表情符号
bash
npm install markdown-it-emojijavascript
const md = require('markdown-it')();
md.use(require('markdown-it-emoji'));
md.render('你好 :smile:'); // 你好 😄脚注
bash
npm install markdown-it-footnotejavascript
const md = require('markdown-it')();
md.use(require('markdown-it-footnote'));
const markdown = `
这是文本[^1]。
[^1]: 这是脚注。
`;
md.render(markdown);定义列表
bash
npm install markdown-it-deflistjavascript
const md = require('markdown-it')();
md.use(require('markdown-it-deflist'));
const markdown = `
术语
: 定义
`;
md.render(markdown);2. 目录 (TOC) 插件
Remark TOC
bash
npm install remark-tocjavascript
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-anchorjavascript
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.jsjavascript
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 prismjsjavascript
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 shikijavascript
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-katexjavascript
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-mathjax3javascript
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-mermaidjavascript
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-chart6. 容器插件
自定义容器
bash
npm install markdown-it-containerjavascript
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-attributesjavascript
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-urlsjavascript
const remark = require('remark');
const lint = require('remark-lint-no-dead-urls');
remark()
.use(lint)
.process(markdown);8. 图片处理插件
图片懒加载
bash
npm install markdown-it-lazy-loadingjavascript
const md = require('markdown-it')();
md.use(require('markdown-it-lazy-loading'));响应式图片
bash
npm install remark-imagesjavascript
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 的功能。如果找不到合适的插件,创建自定义插件也相对简单。
其他资源
- Awesome Markdown - Markdown 资源合集
- Markdown-it 插件列表
- Remark 插件列表
- 统一生态系统 - Remark 背后的框架