计数器停在 0,我差点把一篇空摘要发出去

0 阅读1分钟

写完《三套 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,第三次连返回值都没有 —— 它给你的东西看起来最像成功。

我现在给自己加了一条硬规则:准备发布的任何状态,只要有一个字段我无法确定它是否生效,就不发。

不确定不是「大概率没问题」。不确定就是不知道,而不知道的东西没有默认值。