最好的编辑器升级,是让内容工作不再令人畏惧。
我们在电商 PageBuilder 中加入了 Tiptap React 编辑器,因为原始 HTML 区块的编辑成本已经变得过高。它们确实能正常工作,但只适合那些熟悉标记语言的人。商家应该能够添加标题、图片、按钮、列、标签页或 Instagram 嵌入,而不必担心破坏商品页布局,或发布不安全的 HTML。
这项工作最终落在电商主题的 `codex/htmlblock-wysiwyg-editor` 分支上。首次实现的提交 `08692e2e` 加入了 HTML 区块 WYSIWYG 编辑器。后续提交完善了交互,将标签移入翻译系统,加强了媒体处理,并通过 ID 对媒体库图片进行去重。
Tiptap 很适合这个项目,因为它没有强迫我们采用封闭的 CMS 结构。我们保留了现有的 PageBuilder HTML 契约,并将 Tiptap 用作编辑引擎。
我们构建了什么
从用户可见的角度看,这项功能很简单:PageBuilder HTML 区块现在拥有真正的编辑器,而不再只是原始 HTML 文本框。
但实现内容比这句话听起来更丰富。编辑器支持:
最后一点非常重要。这不是一个从零开始的编辑器。电商前端已经存在 Bootstrap 风格的内容、旧版 CMS 片段、PageBuilder 区域、PrestaShop 媒体路径和 SEO 规则。我们需要的是一个能够编辑内容、同时不扁平化网站现有 HTML 模型的 WYSIWYG 层。
为什么 Tiptap 让事情变得简单
Tiptap 的 React 配置很小:`useEditor`、`EditorContent` 和扩展数组。StarterKit 提供基础编辑能力,然后你可以加入产品所需的功能。
在我们的项目中,依赖集合仍然清晰易懂:
这些已经足够构建编辑器界面,然后在其上叠加我们自己的电商区块。我们使用 `StarterKit.configure()` 配置标准编辑器行为,在需要自定义规则的地方禁用了默认链接处理,然后显式配置 Link 和 Highlight。
简单之处并不在于 Tiptap 解决了所有电商问题。它并没有。简单之处在于它的扩展模型。我们可以直接描述自己的内容节点:按钮、图片、旧版图片、Instagram 嵌入、列、标签页、间隔块、表格、字号、列表样式和文本对齐。
这与 PageBuilder 的结构非常匹配。按钮不只是带样式的文本。图片也不只是一个 `img` 标签。标签页组件包含标签、面板、ID、活动状态和可访问角色。Tiptap 让我们能够将这些内容建模为编辑器内容,而不是事后试图从文本框中推断它们。
HTML 往返转换才是关键
许多 WYSIWYG 迁移失败,是因为编辑器需要一种格式,而网站渲染的是另一种格式。
我们不希望这样。前端已经在商品页、内容页、首页区块、结账相关位置以及其他 PageBuilder 区域中渲染 HTML 区块。编辑器需要加载现有 HTML,让管理员修改它,并保存公共店面能够安全渲染的 HTML。
Tiptap 为此提供了转换工具。对于标签页面板,我们使用 `generateJSON()` 将现有 HTML 转换为编辑器内容,再使用 `generateHTML()` 将面板内容转换回 HTML。这样既能保持编辑界面的结构化,又能保留网站公开输出的 HTML。
这对 SEO 也很重要。CTA 仍然是锚点。标题仍然是标题。图片仍然是带有 alt 文本和标题的语义化 figure。标签页仍然保留可抓取的面板内容。我们没有把电商文案埋进一个只能由客户端运行的组件中,让搜索引擎或辅助技术去猜测其内容。
对商家来说简单,对代码来说严格
管理后台 UI 隐藏了大部分复杂性。
编辑者看到的是工具栏按钮、对话框、布局卡片、图片字段和媒体选择器。他们可以选中文本,并使用浮动 BubbleMenu 进行格式设置。他们可以选择两列或三列布局,而不必记住 Bootstrap class。他们可以添加标签页,并在保存前预览第一个面板。
而底层代码保持严格:
这正是我喜欢这个实现的原因。UI 体验足够宽容,但保存的输出受到控制。
安全性不是可选项
当保存路径信任浏览器返回的任何内容时,富文本编辑器就会变得危险。
我们围绕公共渲染添加了专用的富文本清理层。它使用 DOMPurify,并明确列出允许的标签和属性。在内容进入 React 原始 HTML 接收点之前,它会移除可执行脚本和可执行标记。它还会规范化 Instagram 嵌入数据,并从 URL 属性中移除被阻止的 URL 协议。
渲染测试覆盖了电商页面构建器中真正重要的场景:
这就是“我们加入了一个 WYSIWYG 编辑器”和“我们可以在生产环境中让人们使用它”之间的区别。
分支历史讲述了真实过程
首次提交规模很大:修改了 18 个文件,新增 6,282 行,删除 187 行。它为两个店面应用加入了 Tiptap 依赖、一个 3,000 行的编辑器组件、超过 1,100 行的编辑器 SCSS、渲染测试、管理后台控件、布局处理以及清理路径。
接下来是完善阶段。`d092141d` 改进了 HTML 区块编辑器和导航交互。`320591ec` 增加了英语、瑞典语和法语翻译覆盖,移除了一个媒体选择器 hook,加强了编辑器行为,并让标签页更加灵活。`2c7928b9` 通过 ID 对媒体库图片进行去重。
这就是编辑器开发的真实规律。第一个版本证明编辑器可以存在。后续提交则让它真正可用。
Tiptap 帮助最大的地方
Tiptap 在三个方面提供了很大帮助。
首先,它让编辑器核心变得平淡可靠。我们不需要自行发明选区、命令、撤销/重做、键盘行为或浮动菜单。React 集成和 StarterKit 为我们提供了稳定基础。
其次,它让我们能够对电商专属区块进行建模,而不必将它们隐藏在脆弱的字符串操作中。自定义节点和命令让按钮、图片、列、标签页和嵌入拥有真正的结构。
第三,它让我们能够保留现有的渲染契约。由于公共店面已经依赖 HTML 区块,我们可以继续存储和渲染 HTML,同时在合适的地方于内部使用结构化编辑器内容。
这种组合很少见。许多编辑器在需要自定义输出之前都很容易使用。许多自定义编辑器在需要正常的写作体验之前都很灵活。Tiptap 处于两者之间一个非常实用的位置。
权衡
Tiptap 并不会替你做出产品决策。
你仍然需要决定允许哪些内容类型、哪些 HTML 可以保留、如何选择图片、如何验证链接、粘贴内容应如何处理、翻译如何工作、公共渲染路径如何清理内容,以及应该给予商家多大的自由度。
对于博客编辑器来说,Tiptap 可以快速上手。对于电商 PageBuilder 来说,大部分工作都围绕 Tiptap 展开:旧版兼容性、渲染测试、媒体工作流、可访问性、SEO、翻译和防护规则。
这没有问题。一个优秀的无头编辑器应该提供基础能力,而不是假装你的业务规则不存在。
我的结论
对于这类工作,我还会再次使用 Tiptap。
这个实现让我们获得了一个实用的 Tiptap React 编辑器,同时没有抛弃现有的 PageBuilder 系统。商家获得了更安全的编辑界面。开发者保留了可预测的 HTML。SEO 关键内容仍然可抓取。店面不依赖脆弱的、只能由客户端完成的渲染技巧。
这就是我希望电商管理工具达到的标准:表面简单,输出严格,并且足够平淡可靠,让人们在首次上线后也敢于信任它。
