axe.run() 必须在 DOM 就绪后调用,否则结果为空或漏检;需结合 waitFor/act、cy.get().should() 或 JSDOM 重建确保渲染完成,并显式配置 shadowDom、runOnly(如 wcag2aa)、include/exclude 等参数以覆盖动态内容与合规基线。

axe.run() 扫描前必须确认 DOM 已就绪
直接在页面加载完成前调用 axe.run(),结果大概率为空或漏检——不是没缺陷,而是根本没扫到。常见于 SPA 路由切换后、异步数据渲染完成前、组件挂载未触发 useEffect 的场景。
- React 测试中:用
waitFor或act等待组件完全渲染后再调用axe.run(),否则document.body里可能还是 loading 占位符 - Cypress 中:优先用
cy.get(selector).should('be.visible')确认目标区域已渲染,再执行cy.injectAxe()和cy.checkA11y() - JSDOM 环境:每次测试需新建 JSDOM 实例,复用旧实例会导致缓存 DOM,
axe.run()返回过期结果
runOnly 参数别乱设,WCAG 2.1 AA 是默认底线
全量扫描(默认)耗时长、噪声多;盲目收缩规则又容易跳过关键项。实际项目中应以 WCAG 2.1 AA 为基线,再按需加严或过滤。
- 合规交付必备:
{ runOnly: { type: 'tag', values: ['wcag2aa'] } }—— 这是法律和审计最常引用的最小集合 - 想聚焦高风险问题:可叠加
critical标签,但注意 axe 原生不带该标签,需自定义规则集或改用include指定具体 rule ID(如['color-contrast', 'label']) - 排除已知误报:用
exclude比runOnly更安全,比如第三方富文本编辑器区域可exclude: ['#editor-container']
Shadow DOM 和动态组件必须显式声明
默认情况下 axe.run() 不穿透 Shadow Root,也不等待 Web Component 内部 DOM 就绪。CKEditor、Lit、Stencil 等框架生成的内容极易被跳过。
- 含 Shadow DOM 页面:必须传
{ shadowDom: true },否则<slot></slot>内容、自定义元素内部结构全不可见 - 富文本编辑器(如 CKEditor5):需等编辑器实例 ready 后再扫,且常要加
{ include: ['[data-ck-editor]'] }锁定范围 - 动态加载模块(如 import('./modal.js')):确保模块执行完毕、DOM 插入完成,再调
axe.run(),不能只靠setTimeout碰运气
CLI 集成时路径与上下文最容易出错
本地跑通不等于 CI 里能过。CI 环境缺少浏览器上下文、资源路径错误、HTML 文件未正确生成,都会让 axe 失效。
立即学习“前端免费学习笔记(深入)”;
- 路径问题:
axe-core在 Node 环境下无法直接读取本地 HTML 文件,必须启动轻量服务(如http-server)或用 Puppeteer 加载文件协议 URL - 上下文缺失:Jest + JSDOM 场景下,
global.window必须由 JSDOM 提供,且需手动注入axe到 window 对象(window.axe = axe),否则typeof axe === 'undefined' - 构建产物差异:开发环境 HTML 可能含 source map 注释、热更新脚本,而 prod 构建后 DOM 结构不同,务必对 dist 目录下的真实输出做审计
axe.run(),而是没验证“它到底扫了哪块 DOM”——比如路由懒加载后 main 区域为空、SSR 渲染未激活 JS 导致 ARIA 状态丢失、第三方 SDK 动态插入的弹窗未纳入扫描范围。这些地方不手动 inspect 元素树,光看报告会误判为“已合规”。



















