<main>必须且只能出现一次,因其是页面唯一语义锚点,用于搜索引擎权重分配和屏幕阅读器“M键直达”,重复会触发Lighthouse警告、导致辅助技术忽略后续内容,并破坏可访问性与SEO。

语义化标签在大型项目里不是“锦上添花”,而是降低协作熵值、规避可访问性合规风险、减少SEO返工的刚性基建。
为什么 <main> 必须且只能出现一次
它不是视觉容器,而是告诉浏览器和辅助技术“这里开始是页面唯一主内容区”。搜索引擎会据此分配权重,屏幕阅读器用 main 作为跳转锚点(如按 M 键直达)。多次出现会触发 Lighthouse 可访问性警告 "Document has more than one <main> element",也导致部分读屏软件忽略后续 <main> 内容。
- 实际项目中常见错误:组件库封装了带
<main>的卡片,在循环渲染时重复插入 - 修复方式:只在最外层路由级组件中用一次
<main>,子组件改用<section>或<div role="region"> - CI/CD 中可加 HTMLHint 规则:
"html-req-lang": true和"html-valid-main": true
<article> 和 <section> 的边界在哪
<article> 是自包含、可独立分发的内容单元(比如一篇博客、一条新闻、一个用户评论),脱离当前页面仍能被理解;<section> 是逻辑区块,必须有主题,且通常配一个 <h2>–<h6> 标题。混淆二者会导致结构语义失真,影响爬虫提取准确率。
- 错误示例:把“产品列表页”整个包进
<article>—— 列表本身不是独立内容,每条产品才是 - 正确做法:列表页用
<main>包裹,每个产品项用<article>,分类区块(如“热销榜”“新品推荐”)用<section>并加<h2> - 注意:嵌套
<article>是允许的(如评论嵌套在文章内),但<article>内部不应再出现<main>或<header>(除非是其自身子内容的 header)
表单里不用 <fieldset> 和 <legend> 的真实代价
它们不只是“加个边框”,而是为屏幕阅读器构建语义分组树。缺少 <legend> 时,读屏软件无法告知用户“这组复选框属于哪个功能模块”,用户得逐个听控件名猜意图。
立即学习“前端免费学习笔记(深入)”;
- 典型场景:注册页的“兴趣偏好”多选区域,没
<fieldset>就是一串孤立的<input type="checkbox"> - 修复后效果:读屏软件朗读“兴趣偏好,复选框,科技”——
<legend>提供上下文,<input>提供具体选项 - 兼容性提示:IE11 支持
<fieldset>,但若配合display: contents清除边框,需测试是否破坏语义树(某些旧版 NVDA 会丢弃)
真正容易被忽略的,是语义标签与动态内容的耦合问题:SPA 路由切换时,<main> 的 aria-live 属性要不要更新?<article> 加载完成是否该触发 document.getElementById("article-123").focus()?这些不是“写完就完”的事,而是每次 DOM 更新都要重新校验的链路。



















