前端重构范例:复杂产品架构下的HTML代码质量跃迁

作者:袖梨 2026-07-09
eslint-plugin-jsx-a11y与html-validate必须协同使用:前者专检JSX动态逻辑(如aria-hidden={isMenuOpen ? 'false' : 'true'}),后者覆盖纯HTML片段(如SSR模板、CMS输出),二者互补缺一不可,共同构成全链路无障碍质量门禁。

HTML 代码质量不会靠“重写一遍”自动提升,关键在约束机制和渐进式落地路径。没有统一语义规范、不绑定构建流程、不接入 CI 检查的重构,90% 会退化回老样子。

为什么 eslint-plugin-jsx-a11yhtml-validate 必须同时用

只用 eslint-plugin-jsx-a11y 覆盖不了纯 HTML 片段(如服务端渲染模板、CMS 输出块、遗留 innerHTML 注入),而只用 html-validate 又抓不到 JSX 层面的动态属性逻辑(比如 aria-hidden={isMenuOpen ? 'false' : 'true'} 写反了)。

实操建议:

  • 在 Webpack/Vite 构建中,用 html-validate 的 CLI 模式扫描 src/**/*.htmlpublic/**/*.html,配置 "valid-lang": "error""require-skip-link": "warn" 等强规则
  • 在 ESLint 配置里启用 jsx-a11y/aria-propsjsx-a11y/heading-has-content,并把 rulesDirectory 指向团队自定义规则(例如禁止 div[role="button"] 而未绑定 onKeyDown
  • CI 中两者都设为失败门禁:一个报 aria-required-child 错误,一个报 duplicate-id,都阻断 PR 合并

如何让 data-* 属性不变成新混乱源

重构常把旧 class 名批量替换成 data-componentdata-track-id,结果半年后满屏 data-xxx,语义比 class 还难懂。

立即学习“前端免费学习笔记(深入)”;

实操建议:

  • 限定前缀白名单:data-ui-(仅用于组件边界识别)、data-track-(仅埋点)、data-test-(仅测试定位),其他一律禁止
  • 所有 data-ui-* 必须在设计系统文档中注册,例如 data-ui-tabs-container 对应 Tabs 组件根节点,且该值不可由业务层覆盖
  • 用 PostHTML 插件在构建时扫描非法 data-,匹配正则 /^data-(?!ui|track|test)/ 并报错

嵌套 <section><article> 时,什么情况下必须加 aria-labelledby

<section> 不含标题元素(<h1>–<h6>),或标题是视觉隐藏但未用 aria-label/aria-labelledby 关联时,屏幕阅读器无法建立内容层级,会导致跳过整块区域。

常见错误现象:

  • 仪表盘卡片用 <section class="card"> 包裹,内部只有 <div class="card__title">用户统计</div>(无 heading 标签)
  • 多步骤表单每个 <section> 用 CSS 隐藏标题文字,仅靠图标示意

实操建议:

  • 强制要求:任何无 <h1>–<h6><section><article>,必须有 aria-labelledby 指向页面内真实存在的 ID,且该 ID 所在元素需有可读文本(不能是空 <span aria-hidden="true">
  • html-validate 规则 "require-valid-alt" 扩展为 "require-section-label" 自定义规则,在 CI 中检查
  • 设计系统组件库中,Section 组件默认渲染 <section aria-labelledby={id}>,id 由组件自动生成并注入对应 <h3 id={id}>,业务方不可绕过

真正卡住 HTML 质量跃迁的,从来不是语法对错,而是「谁有权改」和「改错谁负责」——所有校验规则必须绑定到具体角色(设计师提 PR 时触发无障碍扫描,前端提交时触发语义结构检查),否则再好的规则也只是一份 PDF。

相关文章

精彩推荐