发布成功之后:Cherry 崩溃、导入 MD 与改一篇已上线的文章
点下「发布」按钮,页面跳出「发布成功」——如果你做的是自动化发文,到这一步通常就鸣金收兵了。但真正耗时间的往往在后面:回看线上文章,发现代码块少了一半;想改个错别字,编辑器把正文吞了;管理页的「编辑」按钮点完,弹窗被浏览器拦了,不报错也不弹窗。
这篇文章记的是 2026-10-12 一个晚上,我在腾讯云开发者社区的编辑器上连踩的三个坑,以及最后的正解。全部有运行日志为证,代码可以直接跑。
一、坑一:逐字打字,正文被「崩溃」吃掉一半
1.1 环境先认清
腾讯云开发者社区的文章编辑器,底层是 Cherry Markdown:页面上能看到.cherry-editor容器,正文区是 CodeMirror 5(.cm-content),另外还挂着一个隐藏的textarea#code。
自动化脚本要往里面灌一篇几千字的文章,第一反应是「模拟键盘」:Runtime.evaluate拿不到编辑器实例(Cherry 的实例存在闭包里,外部不可达),那就只能用Input.dispatchKeyEvent一个字符一个字符地打。
1.2 诡异的现象
输入过程一切正常:光标跟着走,高亮跟着变,页脚字数一路上涨到 1547。点保存、点发布,也都「成功」了。
然后打开线上文章——正文只剩 720 字,断点恰好落在第一个 shell 代码块中间,就像文章被一把刀齐刷刷切掉一截。
1.3 复盘:不是网络问题,是编辑器崩了
把编辑器崩溃前后的状态拉出来对比,结论很清楚:
- 崩因是 CodeMirror 5 在特定输入序列下 token 解析抛异常。最容易触发的是代码块内以
#开头的行,例如#!/usr/bin/env bash这种 shebang; - 键盘事件是异步的,脚本不等渲染完成就把下一行打了进去,异常之后编辑器的状态机已经乱了,后续输入全部打到一个「半死」的状态里;
- Cherry 的自动保存管线很敬业——它忠实地把这个半截状态保存了上去。于是「输入时看到的 1547 字」和「保存后剩下的 720 字」之间,隔着一次没人看见的崩溃。
第一个「能过」的修法是跟崩溃共处:代码块内逐字符keyDown(text)慢速打,代码块外整行insertText快速灌。实测能过,但每次发布都是赌——赌 CodeMirror 这次不崩。一篇五千字的稿子,赌注是整篇文章。
顺带还踩了一个直觉陷阱:想走捷径,直接给隐藏的textarea#code用setValue赋值再dispatchEvent(new Event('input'))假装用户输入?没用。Cherry 的内容在自己的状态里,外部注入的值根本进不去它的管线,赋值成功,编辑器无动于衷。
二、正解:别再模拟键盘,用编辑器自己的「导入 MD」
2.1 思路转换
编辑器工具栏本来就有一个「导入 MD」按钮。既然编辑器自己有 Markdown 导入管线,为什么要让脚本在外面模拟一个人打字?
点「导入 MD」,背后弹的是一个隐藏的文件选择框:input[accept*=".md"]。让脚本走这条管子,等于借编辑器自己的手灌稿。
2.2 实现:三步
# 第一步:点工具栏「导入 MD」按钮(真实鼠标事件,坐标取可见按钮的 rect 中点)# 第二步:用 CDP 找到隐藏的 file input 并直接塞文件DOM.getDocument# 拿到 input[accept*=".md"] 的 nodeIdDOM.setFileInputFiles# files: ["/abs/path/article.md"], nodeId: <上一步的 nodeId># 第三步:等导入完成,读页脚字数校验对应的页面内判断逻辑:
// 隐藏的 file input 就是导入 MD 的真正入口constfileInput=document.querySelector('input[accept*=".md"]');// 校验信标:页脚的 markdown 字符数(不是 .cm-content 的 innerText)constfooter=document.querySelector('.cherry-editor-footer');constchars=footer.innerText.match(/(\d+)/);console.log('imported chars =',chars&&chars[1]);2.3 为什么这是正解
- 走原生管线:Markdown 的解析、代码块语言标识、表格、列表,全部由编辑器自己的导入逻辑处理,格式保留完整;
- 不产生键盘事件:CodeMirror 崩溃那条路径整个绕开,不存在「赌这次不崩」的期望成本;
- 结果确定、可重复:改稿等于用修好的文件重新导入一次,同样的输入永远得到同样的结果,这对自动化流水线比「手感」重要得多。
还有一个实操细节:校验要看页脚的 markdown 字符数,不要看.cm-content的innerText。后者是渲染视图,代码块折叠、虚拟滚动、语法高亮都会让它「看起来」缺字,拿它当判据会被自己骗。
三、坑二:「存草稿」是个半个陷阱
3.1 场景
稿子写一半,点「存草稿」,心安理得地关掉页面。第二天打开草稿接着改——文章来源的单选按钮三个都没选中,摘要空了,标签空了。正文和标题都在,但那三个发布必填项,一个都没跟上。
3.2 结论
草稿快照里只有正文和标题;文章来源、摘要、文章标签这三项发布必填,不在草稿的保存范围里。重开草稿,等于面对一张重新洗过的表单。
这和上一篇(《表单提交了,但什么都没发生》)是同一个教训的两面:UI 上的「已保存」和「可发布」之间,隔着一次完整的表单校验。草稿让你保存了正文,但没有替你保存「发布的资格」。
3.3 工程对策
把发布三要素写进脚本的入参——标题、关键词、标签、摘要——每一轮发布全量重填,填完再数一遍:单选取值对不对、标签 chips 有几个、摘要长度够不够。不凭记忆,不假设「上次填过」。
// 发布前自检:三要素一个都不能少constsource=document.querySelector('input[value="original"]');constsummary=document.querySelector('textarea.summary, .summary-input');constchips=document.querySelectorAll('.cdc-tags-input__chip');if(!source.checked)return'FAIL: 文章来源未选';if(!summary.value.trim())return'FAIL: 摘要为空';if(chips.length===0)return'FAIL: 标签为空';return'READY';四、坑三:改一篇已经发出去的文章
4.1 常见的后续需求
文章上线了,发现代码块里有个笔误,或者想补一段说明。去管理页找「编辑」——按钮的 onclick 是window.open(...),在自动化场景下,合成调用被浏览器拦了:既没弹窗,也没报错。又是一次「静默丢弃」。
4.2 正解:URL 直导航
根本不用管理页。腾讯云的编辑器 URL 形如:
https://cloud.tencent.com/developer/article/write-new/<articleId>直接导航过去,加载出来的就是这篇文章的可编辑状态。URL 是唯一不会「静默失败」的入口——导航成功与否,看location.href就知道。
4.3 修复流水线:两步
第一步,清空重导。把编辑器清空,用修好的文件重新走一遍「导入 MD」。又是导入 MD——不用键盘,就不会再崩。脚本里先全选删除,再点「导入 MD」,等页脚字数追上新文件的字符数。
第二步,补齐发布。重新填三必填,点「更新发布」(底层走editArticle接口而不是新建),同时轮询网络面板,确认这次发布请求真的发出了、回执是什么。发布后重新打开线上 URL 核对正文——只看编辑器里的状态不算数,判据永远是线上页面。
五、三条军规
- 灌稿走文件导入,不走键盘。凡是编辑器提供「导入文件 / 粘贴 Markdown」能力的,优先用它。模拟键盘是最后手段,且要接受「偶发截断」的期望成本——你看到的字数,不等于保存上去的字数。
- 校验信标选服务端视角的字段。页脚字符数、接口回执、线上页面正文,都可信;DOM 可见文本、UI 状态类名、按钮是否「看起来」点了,都只能当参考。
- 已发内容的编辑入口用 URL 直导航。
window.open、管理页跳转、SPA 的 hash 路由,能绕全绕。URL 是唯一不会「静默失败」的入口。
六、后记
发布成功不是终点。它是这篇文章开始被阅读的时刻,也是自动化真正要跑的里程的开始:修正、回看、再发布。
一个晚上三个坑,修完回头看,每条军规背后都是一次「脚本成功了,事情没发生」。而这类失败最狡猾的地方在于——它不报错。你只能靠判据活着:字节数、回执、线上页面。把判据选对,坑就踩不到第二次。