axe-core必须在真实浏览器中运行,不能静态扫描HTML文件,因其依赖完整DOM和计算样式,需用Puppeteer/Playwright启动Chromium实例加载页面后执行axe.run(),并等待hydration完成、字体就绪,否则会漏检JS动态插入的ARIA属性、语义丢失等问题。

axe-core 必须在真实浏览器中运行,不能只扫 HTML 文件
静态扫描 index.html 会漏掉 70% 以上的可访问性问题:JS 动态插入的 aria-*、SSR 与 CSR 混合渲染后的语义丢失、role 被条件渲染逻辑覆盖等。axe-core 不是文本解析器,它依赖完整 DOM 和计算样式。
常见错误现象:npx axe-cli src/index.html 全绿,但实际页面里屏幕阅读器读不出按钮、heading-order 错乱、color-contrast 在 CI 中突然报错。
- 必须用 Puppeteer / Playwright 启动最小化 Chromium 实例,加载页面后再执行
axe.run() - 不要跳过 hydration 等待:React/Vue 应用需
await page.waitForFunction(() => document.querySelector('#root')?.children.length > 0) - 字体未就绪会导致
color-contrast误报,加await page.waitForFunction(() => document.fonts.check('1em "Inter"'))
CI 中如何让 axe 报告真正阻断构建
axe 默认不抛错,axe.run() 返回的是对象,不是布尔值。很多团队只打印报告却没做断言,等于白跑。
关键参数和写法:
立即学习“前端免费学习笔记(深入)”;
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 设置
failOnViolation: true(Playwright + axe-core 场景下)或在 CI 脚本中手动判断:if (results.violations.length > 0) process.exit(1) - 按严重等级分级响应:critical 错误必须中断,serious 可设阈值(如新增 ≤2 条才通过),warning 不阻断但发 Slack 通知
- 避免 JSON 输出直接塞进日志——用
axe-reporter转成可读文本,并确保含nodes[0].target(CSS 选择器路径),方便定位到具体元素
多路由页面怎么批量测又不超时
只测首页不够,登录页、商品详情页、表单提交后跳转页都可能有独立的无障碍缺陷。但逐个 page.goto() 容易触发 CI 超时或资源耗尽。
实操建议:
- 限定关键路径:用配置文件维护
["/", "/login", "/product/:id"],CI 中展开为实际 URL(如/product/123) - 每个页面单独启动上下文(
browser.newContext()),避免 cookie/session 交叉污染 - 超时设为 30s/页,失败时不中断整个 job,而是收集所有
violations合并输出;最后统一判断 critical 总数是否超标 - 跳过 iframe 内容(除非是核心功能),加
axe.configure({ included: [['body']] })显式限定扫描范围
本地开发时怎么提前拦截 a11y 问题
等 PR 提交后才发现 img-alt-missing 或 duplicate-id,修复成本高且容易反复。应该把检测左移到编辑器保存或 Git 提交阶段。
推荐组合:
- 用
html-validate+husky+lint-staged在pre-commit拦截基础结构问题(attr-req-alt、id-unique) - Vue/React 组件模板也纳入:装
html-validate-vue或自定义 parser,否则.vue里的<template>不被扫描 - 禁止设规则为
warn——"attr-req-alt": "error"才能真正卡住 commit - 对 legacy 页面或第三方嵌入代码,用
ignore字段按 glob 排除,而不是关规则,避免污染全局配置
document.querySelectorAll('[role="button"]') 这类泛选择器,说明扫描范围没收敛;violation.nodes 为空却报错,大概率是 hydration 没等全。这些细节不抠清楚,自动化就只是个摆设。


















