文章摘要
不到 40 行代码,用正则和占位符把 Markdown 转成 HTML,顺便搞懂为什么代码块要先"藏起来"。
博客后台需要一个把 Markdown 转成 HTML 的功能。引一个 marked 当然可以,但这个转换本身只有几十行,值得自己写一遍。
难点不是替换,是顺序
如果直接按顺序做正则替换,代码块里的星号会被当成加粗,链接里的括号会被图片语法吃掉。解决办法:先把代码块和图片抽出来换成占位符,等所有行内替换做完再塞回去。
JS
const store = [];
const hold = html => { store.push(html); return '\0' + (store.length - 1) + '\0'; };
const FENCE = '`'.repeat(3);
src = src.replace(new RegExp(FENCE + '(\\w*)\\n([\\s\\S]*?)' + FENCE, 'g'),
(m, lang, code) => hold('<pre><code data-lang="' + lang + '">' + esc(code) + '</code></pre>'));块级语法
标题、引用、列表都是行首锚定的正则,注意 m 标志让 ^ 匹配每一行开头:
JS
html = html.replace(/^###\s*(.+)$/gm, '<h3>$1</h3>');
html = html.replace(/^##\s*(.+)$/gm, '<h2>$1</h2>');
html = html.replace(/^>\s*(.+)$/gm, '<blockquote>$1</blockquote>');
html = html.replace(/^-\s+(.+)$/gm, '<li>$1</li>');
html = html.replace(/(<li>[\s\S]*?<\/li>)/g, m => '<ul>' + m + '</ul>');别忘了转义
顺序是:先转义,再解析。用户写的 <script> 必须先变成 <script>,否则解析器就是个 XSS 生成器。
唯一例外是占位符本身。用 \0 包住的数字不可能出现在转义后的文本里,最后一步统一还原即可。
剩下的坑
- 嵌套列表要递归,行首空格数决定层级
- 表格没有标准语法,不同实现差异最大
- 行内代码里的星号不该被解析,得给它比行内替换更高的优先级
写完这一套大概 40 行,够个人博客用了。真正的复杂度在容错,而容错靠的是测试用例,不是更聪明的正则。
留言板0
昵称必填 · 邮箱仅用于生成头像
还没有留言,来坐第一排
1231231😭
第一条留言,测试<script>alert(1)</script>