Markdown 表格渲染 Bug 修复记录

Markdown 表格渲染 Bug 修复记录

上线第二天就被打脸:用户反馈表格显示异常。原因是我自己写的轻量 Markdown 转换器没支持表格语法。这篇把诊断、修复、验证全过程拆开讲。

Bug 现场

打开「WordPress 自动发布」那篇文章(post 130),里面两张表格完全错乱:

预期 实际
整齐的表头、边框、行高一致 文本堆在一起,分隔行 `

原表格 Markdown:


| 方案 | 入口 | 优点 | 缺点 |
|------|------|------|------|
| REST API | /wp-json/... | 现代、官方推荐 | 鉴权配置复杂 |
| XML-RPC | /xmlrpc.php | 老牌、协议简单 | 部分主机默认关闭 |

预期渲染:HTML

结构。

实际渲染:每一行(包括 | --- | 分隔行)都被当

段落输出。

根因分析

打开 wp_publish.pymd_to_html 函数,状态机里只处理了:

  • 代码块(“`)
  • 标题(#)
  • 引用(>)
  • 有序/无序列表
  • 段落

没有表格分支。这是设计取舍:上一篇文章里我写过「复杂的 Markdown 语法(表格、嵌套列表、HTML 混排)我故意没支持」,但表格其实是高频需求,不算复杂

定位只花了 2 分钟。md_to_html 是单文件单函数 ~80 行,没有调用第三方库,状态机清晰,问题边界明确。

修复方案

GitHub 风格表格语法规则:

  1. 第一行:表头,单元格用 | 分隔
  2. 第二行:分隔行,每个单元格是 :---(左对齐)、:---:(居中)、---:(右对齐)或 ---(默认)
  3. 第三行起:数据行,列数应对齐表头

伪代码:


读表头行 → 读分隔行(确认是表格)
循环读数据行 → 不是表格行就结束
调用 _render_table 渲染

输出 HTML 用

,这是 WordPress 古腾堡编辑器原生表格块使用的 class,主题样式会自动套用(边框、斑马纹等)。

关键代码

加 3 个辅助函数 + 主循环里加一段状态:


def _parse_table_row(line):
    """ | a | b | -> ['a', 'b'] """
    s = line.strip()
    if not (s.startswith("|") and s.endswith("|")):
        return None
    return [c.strip() for c in s[1:-1].split("|")]


def _is_table_separator(line):
    """ | --- | :---: | -> True """
    s = line.strip()
    if not (s.startswith("|") and s.endswith("|")):
        return False
    cells = [c.strip() for c in s[1:-1].split("|")]
    sep_re = re.compile(r"^:?-{3,}:?$")
    return all(sep_re.match(c) for c in cells)


def _render_table(rows):
    """ rows[0] 是表头,rows[1:] 是数据行 """
    out = ['<table class="wp-block-table">']
    out.append("<thead><tr>")
    for cell in rows[0]:
        out.append(f"  <th>{inline(cell)}</th>")
    out.append("</tr></thead>")
    if len(rows) > 1:
        out.append("<tbody>")
        for row in rows[1:]:
            # 列数对齐:不足补空,多余截断
            cells = list(row) + [""] * max(0, len(rows[0]) - len(row))
            cells = cells[: len(rows[0])]
            out.append("<tr>")
            for cell in cells:
                out.append(f"  <td>{inline(cell)}</td>")
            out.append("</tr>")
        out.append("</tbody>")
    out.append("</table>")
    return "\n".join(out)

主循环里新增:


# 进入表格:当前行是表头,下一行是分隔行
if not in_table:
    cells = _parse_table_row(stripped)
    if cells and i + 1 < len(lines) and _is_table_separator(lines[i + 1].strip()):
        in_table = True
        table_rows = [cells]
        i += 2  # 跳过表头 + 分隔行
        continue

# 数据行
if in_table:
    cells = _parse_table_row(stripped)
    if cells:
        table_rows.append(cells)
        i += 1
        continue
    else:
        # 表格结束
        close_table()

测试用例

6 个场景全部通过:

场景 验证点 结果
基础表格 + 周围段落 表格前后段落、列表、引用正确分隔 通过
对齐语法 :--- :---: ---: 3 种对齐标记都能识别 通过
表格中含行内语法 code粗体斜体、链接在单元格内生效 通过
单列表格 边界场景不崩溃 通过
列数不对齐 不足补空、过多截断 通过
表格前后是列表 列表和表格不互相打断 通过

上线验证

直接在「呼叫吉米」专栏发一篇新文章,把上面 6 个测试场景对应的 Markdown 全部塞进去,看实际渲染效果。

工具自身的验证方法:转换后的 HTML 里 grep

,能匹配到说明解析成功。

经验总结

  1. 「故意不支持」要写明适用范围。上一篇文章我说「表格不支持」是基于当时的判断,但表格其实是高频需求,门槛比想象低。
  2. 状态机扩展要保持单一职责。表格状态(in_tabletable_rows)跟列表/引用/代码块状态并列,互不干扰。
  3. 先写测试再发版。这次修复我先跑了 6 个本地测试用例再发布,避免「线上修线上」的尴尬循环。
  4. class 名要跟主题对齐。用 wp-block-table 而不是自定义 class,让古腾堡/主题的 CSS 自动接管,省去写样式的麻烦。

—— 呼叫吉米


已发布

分类

,

来自

标签: