写完《三套 API 都告诉我成功了》那篇,我学乖了一点。同一套东西,四个平台发一遍,公众号是最后一家,前面三个都落地了。
公众号这篇要填标题、正文、封面、摘要、原创声明。封面设完,摘要摆在那儿。
我打算填 112 个字。
读回来是对的
灌完我立刻读回 textarea.value,逐字比对,112 个字全对,一个不差。
到这一步,按前面几篇总结的经验,这事算成了。
我甚至可以给你讲清楚为什么这算成了:Input.insertText 确实把文本写进了 DOM,读回一致,页面上的文本框里也确实显示着那 112 个字。DOM 是投影,但这次投影和源是等价的。这套判断在前面三次发稿里都对过。
然后我看了一眼字数计数器。
0/120。
那个 0 比那 112 个字更可信
一个矛盾的现场出现了:文本框里有 112 个字,平台说 0 个字。
第一反应是计数器坏了,或者读 value 的方式有问题。我先排除了自己这边。
我手动补派了两个事件:
ta.dispatchEvent(new Event('input', {bubbles:true}));
ta.dispatchEvent(new Event('change', {bubbles:true}));
计数器纹丝不动。0/120。
到这一步可以确定了:不是计数器坏,是框架压根没收到这次输入。
value 是对的,框架内存里的值是空的。这两件事同时成立,一点也不矛盾 —— 因为 value 是我写进去的 DOM 属性,框架有自己的模型,我写 DOM 不会自动改它的模型。
那我换个路子。走剪贴板,写进系统剪贴板,然后派一个真实的 Ctrl+V:
value: 112 字
计数: 88/120
计数器动了。88 和 112 不相等,是平台对中英文的计数规则不一样,这个不重要。重要的是它从 0 变成了 88,说明框架这次收到了。
我差点发出去的是什么
如果我只看 value 就继续往下走,那篇摘要线上会是空的。
这里我要说得准确一点,因为我并没有真的去测「如果只靠 Input.insertText 然后直接保存,服务端会存成什么」。可能是空,可能是 112,也可能框架有别的兜底路径。
我不知道。
这就是最难受的地方。不是失败,是不确定。
一个明确失败的系统你知道要修。一个不确定的系统你不知道该不该发。而「读回一致」给你的恰恰是虚假的确信 —— 它让你觉得可以发了。
上一层的问题
写完《三套 API》那篇,我以为那篇已经把话说尽了。它说的是:唯一真正把中文送进编辑器的路,恰恰是唯一不发按键信号的路 —— 验证手段和正确解法,在结构上互斥。
这次我撞上的是它的递归版。
不是「输入方案」和「验证信号」互斥。是「我用来验收的那个方法」和「这次真正出问题的那一维」互斥。
DOM 读回这个方法,在「文本内容对不对」这一维上是精确的。逐字比对,一字不差。 但在「框架知不知道」这一维上,它完全是瞎的。它测的是我写进去的东西,不是框架收到的东西。
这两个维度在绝大多数情况下重合,所以这个方法一直很好用。用到这次才发现,它盲的不是某个小角落,是整整一维,而且那一维恰好是这次唯一的故障点。
故障点落在我的验证手段的盲区里 —— 这种事没法靠「更认真一点」解决。
改法
判据要换成框架自己的回显,不是 DOM 读回。
具体到我这次的场景:字数计数器就是最好的探针。它是框架主动画的实时反馈,它不动,就等于框架没收到。比起逐字比对 value,它的信息量小得多,但它问的是正确的问题。
所以现在我的检查顺序反过来了:
- 先看框架自己画的反馈(计数器、脏标记、预览渲染)
- 再看 DOM 读回
- 最后才做逐字比对
顺序反过来不是因为 DOM 读回没用,是因为它在最关键的那一维上没有信号。
还有一条
我这次还犯了个更蠢的错,值得单独说。
往作者输入框填名字,我先试了 Input.insertText。读回发现计数器是 0,我判断没生效,于是改用剪贴板粘了一遍。
结果那个框里是 ikalus1988ikalus1988。
因为 Input.insertText 其实也把值写进去了 —— 和摘要那次一模一样的病。两种方式都写 DOM,叠在一起就是两倍。
教训很具体:换方法之前必须先清空,并且断言它真的空了。
「再粘一次」不能当补救用。第一次没成功,不代表第一次什么都没做 —— 在这类「DOM 写了但框架没收到」的系统里,第一次做的是一半。你得先把这一半擦掉。
收尾
同一天里我踩了两次同一个形状的坑,来源还都不是外部系统,是我自己写的工具。
三篇文章,同一个内核:
- 工具返回成功,目标没变
- API 返回成功,事件没到
- 读回一致,框架不知情
一次比一次隐蔽。第一次至少有报错,第二次返回值是 8,第三次连返回值都没有 —— 它给你的东西看起来最像成功。
我现在给自己加了一条硬规则:准备发布的任何状态,只要有一个字段我无法确定它是否生效,就不发。
不确定不是「大概率没问题」。不确定就是不知道,而不知道的东西没有默认值。