用无头浏览器验收 UI:四个让我看图看错半天的陷阱
我不太信任"改完直接说好了"这件事。前端改动至少要截一张图自己看,否则等别人告诉你不满意的时候,成本就高了。
但这套自动化截图用下来,踩到的坑比 UI 本身还多。记一下,主要是提醒自己:看图之前先量数。
一、--window-size 不等于布局视口
Edge / Chrome 的 headless 截图里,--window-size=420,860 并不代表页面 innerWidth 是 420。窗口包含边框等界面装饰,实际布局视口要小一圈。
我实测这台机器上的固定偏差是 (+24, +139):想要 876×430 的布局视口,窗口得传 900,569。
这直接导致过一次误判:页面在 420 宽下看起来有横向溢出,我花时间改了半天布局,后来量了一下 document.documentElement.scrollWidth,发现是 477,而视口只有 452——数字确实溢出了,但原因是我按错误的视口宽度在算比例,改的方向全错。
结论:不要靠肉眼判断"有没有溢出",直接量。
二、--virtual-time-budget 会跳过异步加载
这个参数的本意是"虚拟时间跑满这么多久就截图",用来等动画。但它会把 setTimeout / setInterval 立刻推进到结束状态。
后果是:动态插入的 <script> 还没下载执行,轮询已经把次数跑完,截图里就是"点了按钮没反应"。
第一次遇到时我以为是功能坏了,改了半天代码,其实页面在真机上完全正常。
正确做法是等节点出现,而不是等时间:
function waitNode(selector, timeout = 8000) {
return new Promise((resolve, reject) => {
const hit = document.querySelector(selector);
if (hit) return resolve(hit);
const obs = new MutationObserver(() => {
const el = document.querySelector(selector);
if (el) { obs.disconnect(); resolve(el); }
});
obs.observe(document.documentElement, { childList: true, subtree: true });
setTimeout(() => { obs.disconnect(); reject(new Error('timeout: ' + selector)); }, timeout);
});
}
MutationObserver 不受虚拟时间影响,节点一出现立刻回调。
三、截图路径必须是 ASCII 绝对路径
这个坑特别安静:路径里带中文,或者用了相对路径,浏览器不报错,就是不生成文件。
第一次我盯着 ls 输出愣了半分钟,因为命令退出码是 0。
现在我一律输出到临时目录的纯英文文件名:
msedge.exe --headless=new --disable-gpu --no-sandbox \
--user-data-dir="C:/tmp/edge-profile" \
--window-size=900,569 \
--screenshot="C:/tmp/shot.png" \
"http://127.0.0.1:8799/page.html"
中文路径只在"读取"的时候还能用,写文件一律绕开。
四、把测量结果写进 document.title
比截图更可靠的验收方式,是让页面自己报告布局状态。因为截图只能看"像不像",getBoundingClientRect 能告诉你"是不是"。
我在测试页里注入一段探针,把关键数值塞到 document.title:
const r = document.querySelector('.dialog').getBoundingClientRect();
document.title = `GEOM VW=${innerWidth} L=${r.left.toFixed(1)} R=${(innerWidth - r.right).toFixed(1)}`;
然后用 --dump-dom 把 DOM 抓出来,grep 一行标题就行:
msedge.exe --headless=new --dump-dom "http://127.0.0.1:8799/probe.html" \
| grep -o "<title>[^<]*</title>"
<title>GEOM VW=876 L=18.0 R=18.0</title>
左右边距相等,说明真的居中;数值为负,说明真的溢出。这比看截图准得多,而且可以放进脚本做断言。
五、真遇到"颜色看不见"这种问题,也要量
有一次登录页的文字在浅色背景上几乎看不到,截图里就是一片浅色。我以为是背景透明度问题,改了两轮都不对。
最后用 getComputedStyle 打印出真实颜色:
const cs = getComputedStyle(el);
document.title = `C=${cs.color} BG=${getComputedStyle(parent).backgroundColor}`;
结果是 rgb(156, 171, 188)——一条来自另一个样式文件的在线态颜色,被优先级更高的规则覆盖了。肉眼只会觉得"字很淡",但量一下就立刻知道是哪个色值、该改哪一层。
小结
这套流程现在的样子:
- 起本地静态服务
- 打开探针页(写入固定存档 / 展开指定面板)
MutationObserver等目标节点- 用
getComputedStyle/getBoundingClientRect量数值,写进document.title --dump-dom读数值做断言- 最后再截图,只用来确认"观感",不用来判断"对错"
顺序很重要:先用数字确认结构对不对,再用图片确认好不好看。 反过来就会拿着假象去改真代码。