AutoFillingForm 开发日志:从录制脚本到可校验的自动填单流程
AutoFillingForm 是一个基于 Chrome Manifest V3 的自动填单插件原型。它要解决的不是“把几个固定字段写死填进去”,而是把真实业务表单里的登录、跳转、跨标签页、弹窗、输入、勾选、提交前检查这些流程录制下来,再用结构化数据驱动回放。
这篇文章记录它的开发过程。重点放在工程链路:插件如何捕获页面操作、如何把录制值变成可维护的数据模板、如何在回放前后做校验,以及如何让暂停、停止和速度配置这些看似边缘的交互真正可靠。
1. 项目目标:把人工填表变成可复用流程
自动填单工具很容易做成一个脆弱脚本:固定几个 selector,按顺序写值,遇到页面稍慢或弹窗切换就失败。AutoFillingForm 的目标更接近一个轻量流程引擎:
- 录制模式:记录点击、输入、下拉、勾选、键盘、滚动,以及跨标签页或弹窗中的动作。
- 填充模式:按录制脚本回放,并用
从自定义数据中替换字段值。 - 校验模式:读取页面真实字段值,与期望数据逐项对比,并在页面上标注不一致字段。
- 脚本管理:支持本地保存、JSON 导入导出,以及预留云端下载和上传接口。
因此它的核心不是单次自动化,而是让“录制一次、替换数据、多次执行、可视化校验”成为稳定闭环。
2. 架构分层:后台编排,页面执行,弹窗控制
项目结构按 Chrome 插件的职责拆分得比较清晰:
background/ Service Worker:录制会话、回放、校验、存储和消息入口
content-script/ 页面侧脚本:事件捕获、元素定位、动作执行和视觉标注
popup/ 插件弹窗:录制、保存、填充、校验、继续和停止控制
options/ 配置页:脚本、数据、云端服务和高级速度参数
sample-data/ 示例自定义数据background/index.js 更像消息路由和状态入口;recorder-manager.js 负责把录制动作整理成脚本;replayer.js 负责编排回放;validator.js 负责校验;真正触碰页面 DOM 的逻辑则放在 content-script/player.js、recorder.js 和 highlighter.js。
这个分层有一个实际收益:后台脚本可以保存跨页面流程状态,content script 专注当前页面 DOM,popup 和 options 只承担控制面板职责。后续修复速度、暂停、校验时,也能沿着这条边界定位问题,而不是把状态散在每个 UI 按钮里。
3. 录制:从用户操作到结构化 action
录制阶段的关键是把浏览器里的自然操作转成可重放的 action。输入框、选择框、复选框、单选框看似都属于“表单字段”,但它们的语义并不相同。
普通输入可以记录为:
{
"type": "input",
"params": {
"value": "示例科技有限公司",
"clearFirst": true
}
}复选框和单选框则不能只记录 true 或 false。例如一组爱好复选框的业务数据通常是 ["sports"],不是“第三个 checkbox 被勾选”。因此录制侧需要保留选项本身的 value:
{
"type": "check",
"params": {
"checked": true,
"value": "sports"
}
}这个细节后来成为回放和校验稳定性的基础:对 checkable 字段,系统比较的是“某个 value 是否被选中”,而不是裸布尔值。
4. 数据模板:录制值不能停留在脚本里
如果录制脚本里直接保存用户第一次输入的真实值,脚本很快会失去复用价值。AutoFillingForm 的做法是在停止录制后自动生成 dataSchema,并把字段动作绑定到模板变量。
录制脚本会从:
{
"type": "input",
"params": {
"value": "示例科技有限公司"
}
}变成:
{
"type": "input",
"params": {
"value": "{{companyName}}"
}
}同时 popup 的“自定义数据 JSON”会自动填入一份可编辑的默认数据:
{
"companyName": "示例科技有限公司",
"hobby": ["sports"]
}这一步看似是体验优化,实际上决定了插件能否进入真实使用场景。用户不需要从零编写完整 JSON,只需要在录制生成的默认数据上修改值。
5. 模板解析:保留 JSON 类型比字符串替换更重要
模板替换最容易犯的错是把所有东西都当字符串。例如 如果对应 ["sports"],错误实现会把数组变成字符串 "sports" 或 "[\"sports\"]",最终复选框匹配就会失败。
因此回放和校验都需要区分两种情况:
- 整个值就是一个模板表达式:返回原始 JSON 类型。
- 模板只是字符串的一部分:按字符串插值处理。
概念上可以理解为:
if (value === '{{hobby}}') {
return userData.hobby
}
return value.replace(/\{\{([^{}]+)\}\}/g, (_, key) => String(userData[key.trim()] ?? ''))这个规则让数组、布尔值、对象等结构化数据可以穿过模板层,继续被后面的字段语义正确消费。对自动填单来说,这比“替换成功”本身更关键。
6. 回放:动作执行不是简单的 for 循环
回放表面上是按顺序执行 action,实际要处理页面加载、DOM 变化、事件触发和用户介入。background/replayer.js 负责流程编排,content-script/player.js 负责页面内动作执行。
页面侧需要做几类事情:
- 等待元素出现,而不是假设 selector 立即可用。
- 输入后触发合适的 DOM 事件,让页面框架感知变化。
- 对字段值做稳定性确认,避免刚写入就被页面脚本覆盖。
- 对 checkbox / radio 按
type + name + value找到具体选项。 - 在失败时把上下文反馈给后台,决定重试、暂停或终止。
这一层后来还承担了速度优化:输入字符间隔、视觉延迟、字段稳定等待、轮询间隔等都不能写死在代码里,否则不同页面之间会很难调平。
7. 校验:用户需要知道哪里不对,而不是只知道失败了
校验模式的目标不是返回一个笼统的 false。它要告诉用户哪些字段和期望不一致,并在页面上可视化标注。
实现上,background/validator.js 读取脚本和自定义数据,解析出每个字段的期望值,再让 content script 获取页面真实值。校验结果通过 content-script/highlighter.js 标在页面中,同时 popup 显示阶段状态。
这里有两个重要迭代:
- 字段读取改成按 tab 批量执行,避免逐字段串行等待带来的明显慢感。
- 校验事件带上阶段文案,例如
提交暂停前校验、暂停前校验、结束前校验,让用户知道当前不是卡住,而是在做哪一步检查。
校验结果也必须跟回放语义一致。对 checkbox / radio,实际值可能是 { checked: true, value: "sports" } 这种结构,比较时要按选项值判断是否应该选中,而不是把它格式化成 sports: true 后再跟数组或字符串硬比。
8. 暂停提交:自动化流程里的人工确认点
真实业务表单并不总是适合自动提交。很多场景需要插件先填完字段,然后让用户检查、手动提交,再继续后续动作。
AutoFillingForm 为这种场景加入了手动提交暂停:
- 普通提交在关闭自动提交时会暂停。
- 页面上显示
AutoFill 填充已完成,提交后继续。 - 暂停前先执行一次校验,让用户看到字段是否有问题。
- 用户提交后,可以在 popup 中点击“继续执行”。
- 如果点击“停止执行”,任务必须立即终止,而不是卡在暂停 promise 里。
这类交互的难点不是显示一个按钮,而是状态机要干净。暂停等待、继续、停止、校验、最终完成都可能交织在一起;停止操作尤其要被当成硬终止,不能被后续恢复逻辑误判成可重试错误。
9. 速度配置:把隐藏等待变成可调参数
自动化速度不是一个简单的 delay。如果只调动作间隔,页面侧的输入稳定等待、change 事件等待、元素轮询和重试延迟仍然可能让整体流程变慢。
项目把速度配置集中到 options 页,并通过 chrome.storage.local 传递到后台和页面侧。可配置项包括:
- 动作间隔
- 视觉反馈延迟
- 输入字符延迟
- 页面稳定等待
- 字段稳定等待
- 输入校验稳定时间
- change 校验稳定时间
- 重试延迟
- 轮询间隔
popup 仍保留快速控制入口,options 页则承担高级参数的来源。这种分工比较适合实际调试:现场执行时快速切换速度,深入排查时再调细粒度等待。
10. Popup:小窗口也要有清楚的信息架构
Chrome 插件 popup 空间很小,按钮文案和顺序会直接影响操作信心。最终控制区按两行组织:
保存数据 开始填充 开始校验
继续执行 停止执行这个排列把常规动作和运行中动作分开:保存、填充、校验是启动前操作;继续和停止是流程暂停或运行中的控制。
实现时保留了既有按钮 ID,只调整 DOM 顺序和 CSS 分组。这样可以避免为了 UI 排列改动事件绑定,降低回归风险。
11. 验证方式:插件项目也需要小步可证
Chrome 插件的完整验证依赖浏览器环境,但很多核心逻辑可以用更轻的方式先压住风险:
- 对修改过的 JS 文件执行
node --check,确认语法和模块解析无误。 - 用小型 Node 片段直接测试模板解析、checkbox/radio 匹配和暂停停止状态。
- 用 grep 追踪残留硬编码等待,避免只改了显眼的一处。
- 对 popup 文案和按钮顺序做字符串级确认,减少 UI 回退。
这不是完整替代浏览器测试,但能让每次迭代都留下基本可信的证据。尤其是在扩展页面无法被某些浏览器自动化工具直接打开时,静态检查和针对性逻辑测试就更有价值。
12. 复盘:自动填单的核心是可解释的稳定性
AutoFillingForm 的开发过程里,最有价值的不是某个单独功能,而是几个工程原则逐渐收敛:
- 录制脚本要从一开始就面向复用,不能只保存第一次输入的字面值。
- 模板替换必须保留 JSON 类型,否则 checkbox、多选和布尔字段都会在边界处失真。
- checkbox / radio 的语义是
type + name + value,不是简单的 checked 布尔值。 - 校验要在用户最需要判断的时候出现:暂停前、提交前、结束前。
- 停止操作必须是硬终止,尤其是在暂停等待状态下。
- 性能优化要追完整链路,隐藏等待比显式动作延迟更容易拖慢体验。
- Popup 和 options 的职责要分清:前者适合即时控制,后者适合高级配置。
自动填单看起来是“让机器帮人点页面”,但真正可靠的版本更像一个可观察、可调试、可暂停、可校验的小型工作流系统。只有当用户能看清当前阶段、知道字段哪里不对、能随时停下来接管,自动化才不会变成新的不确定性来源。