Markdown 编辑器
用正则把 Markdown 语法实时转成 HTML,左侧输入右侧预览。第一次真正让字符串处理长出肌肉记忆的项目。
左侧敲字,右侧就变 HTML
这个项目第一次让我正儿八经写正则。# 一级标题、**加粗**、[链接](url)、`代码`、引用块、列表……每一种语法都是一条规则,每条规则就是一行正则。看上去只是字符串替换,但真写起来「贪婪匹配吞了后面所有字符」「转义顺序不对导致前面规则破坏后面规则」会让你怀疑人生。
分五步把 Markdown 跑起来
不要试图一次写完所有语法,从最简单的标题开始,逐条加规则,每加一条都立刻测试。
双栏布局 + 输入监听
左右两栏 grid,左是一个 <textarea>,右是一个 <div class="preview">。给 textarea 绑 input 事件,每次按键都调 render() 把转换后的 HTML 写进右侧。先用纯文本透传,确认事件通路 OK。
先转义 HTML,再做替换
这是最重要也最容易被忽略的一步:先把用户输入里的 < > & 替换成 < 等实体,防止用户输入 <script> 直接执行(XSS)。这一步必须在所有 Markdown 规则之前做,否则后面规则生成的标签也会被转义掉。
逐条加规则,注意顺序
顺序原则:先处理块级(标题、引用、列表、代码块),再处理行内(加粗、斜体、链接、行内代码)。因为行内规则可能匹配到块级语法里的字符。比如先转 **粗体** 再转 `code`,否则行内代码里的 ** 会被错误地变成粗体。
贪婪 vs 非贪婪
写 **粗体** 规则时,新手会写 /\*\*(.+)\*\*/g,结果是贪婪匹配——一行里有两个粗体时,第一个 ** 会一直吃到最后一个 **。改成 /\*\*(.+?)\*\*/g(加个 ?)就是非贪婪,匹配最短可能。这是这个项目最大的坑。
支持代码块与列表
代码块用 /```([\s\S]+?)```/g 匹配三反引号包裹的内容,[\s\S] 是跨行匹配的小技巧(点号 . 不匹配换行)。列表先按行 split,连续 - 开头的合并成一个 <ul>。做完这两步,编辑器就基本能用了。
关键代码片段
核心转换函数,按块级 → 行内的顺序处理,每条规则都单独成行方便排错。
function md2html(src) { // 1. 先转义 HTML,防止 XSS let s = src .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>'); // 2. 块级:代码块(先做,避免被行内规则破坏) s = s.replace(/```([\s\S]+?)```/g, (m, code) => `<pre><code>${code.trim()}</code></pre>`); // 3. 块级:标题 s = s.replace(/^### (.+)$/gm, '<h3>$1</h3>') .replace(/^## (.+)$/gm, '<h2>$1</h2>') .replace(/^# (.+)$/gm, '<h1>$1</h1>'); // 4. 块级:引用 s = s.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>'); // 5. 行内:加粗、斜体、行内代码、链接(注意非贪婪 ?) s = s.replace(/\*\*([^*]+?)\*\*/g, '<strong>$1</strong>') .replace(/\*([^*]+?)\*/g, '<em>$1</em>') .replace(/`([^`]+?)`/g, '<code>$1</code>') .replace(/\[([^\]]+?)\]\(([^)]+?)\)/g, '<a href="$2">$1</a>'); return s; }
正则不是玄学,是「字符串版的乐高」。这个项目让我学会一件事:写正则之前先在 regex101.com 上用真实样本测一遍,把每条规则的匹配范围画出来,比盯着代码空想快十倍。所有「我这条正则怎么不工作」的疑惑,在可视化面前都会立刻现形。