<main>是页面唯一主内容锚点,决定读屏器跳转、SEO摘要及Lighthouse评分;多个<main>会致NVDA/VoiceOver行为不可预测,违反WCAG 2.1“每页仅一个主地标”要求。

<main></main> 是页面唯一、不可复用的主体内容锚点,不是视觉容器,也不是可选装饰——它直接决定屏幕阅读器跳转路径、搜索引擎摘要提取范围,以及 Lighthouse 的无障碍评分。用错或不用,可访问性与 SEO 会实质性受损。
为什么多个 <main></main> 会破坏可访问性
辅助技术(如 NVDA、VoiceOver)把 <main></main> 当作“按 M 键直达主内容”的唯一锚点。一旦出现两个,读屏器可能随机选一个、跳过全部,或反复播报“主内容开始”,用户完全无法预测行为。
- SPA 中每个路由组件都写
<main>{children}</main>→ hydration 后 DOM 留下多个<main></main>,Lighthouse 报duplicate-main - 服务端模板未做条件判断,列表页和详情页共用同一套
<main></main>包裹逻辑 → 渲染出嵌套或并列的多个 - 浏览器 DevTools 不报错,但 axe 测试直接标红,WCAG 2.1 明确要求 “one main landmark per page”
<main></main> 里能放什么、不能放什么
判断标准极简:如果这块内容复制粘贴到另一个页面还能原样用,那它就不该进 <main></main>。
- ✅ 允许:
<article></article>(博客正文)、<ol></ol>(商品列表)、<form></form>(独立提交表单)、纯文本段落 - ❌ 禁止:
<nav></nav>(全局导航)、<footer></footer>(页脚版权)、<aside></aside>(侧边推荐)、<header></header>(网站 Logo + 主导航) - ⚠️ 注意:
<header></header>作为<main></main>的子元素是允许的——但仅限“本篇文章的标题与元信息”,不是整个网站头部
SPA 路由中如何安全更新 <main></main>
React/Vue 里最稳妥的做法是:只在根 Layout 组件中声明一次 <main></main>,所有子路由内容作为其直接子节点动态替换,而不是让每个页面组件自己渲染一个 <main></main>。
立即学习“前端免费学习笔记(深入)”;
- ✅ 正确:用 React Router v6 的
<outlet></outlet>套在<main></main>内,或 Vue Router 的<routerview></routerview>作为<main></main>唯一子元素 - ❌ 错误:在
ArticlePage里写<main><article></article></main>,又在ProfilePage里重复 - ⚠️ 特殊情况(如 404 页面):不能靠
<main>{error ? <notfound></notfound> : <page></page>}</main>,要确保整个生命周期内<main></main>节点只存在一个实例,可通过状态控制是否渲染该节点本身
最容易被忽略的一点:它不是视觉容器,不带默认样式。很多人加了 <main></main> 却没配 CSS,结果发现布局没变化——这恰恰说明它干的是语义活,不是排版活。而真正翻车的,往往发生在 SSR 模板结构断裂、或动态插入时旧版读屏工具忽略语义的边界场景。



















