必须且只能出现一次,是页面当前视图核心内容的唯一语义锚点;它不控制样式或布局,仅标识随URL变化、不可复用、构成页面存在根本理由的内容,重复使用会破坏可访问性、违反WCAG 2.1及HTML5规范。

<main></main> 必须且只能出现一次,是页面“当前视图核心内容”的唯一语义锚点。它不控制样式,也不参与视觉布局,只回答一个问题:**这块内容是否随 URL 变化、不可复用、且构成该页面存在的根本理由?** 否则,就别放进去。
为什么多个 <main></main> 会直接破坏可访问性
屏幕阅读器(如 NVDA、VoiceOver)把 <main></main> 当作按 M 键直达的唯一入口。出现两个时,行为完全不可预测:可能跳过全部、随机选一个、或反复播报“主内容开始”。Lighthouse 会报 duplicate-main,axe 工具直接标红,WCAG 2.1 明确要求 “one main landmark per page”。
常见翻车场景:
- React/Vue 中每个页面组件都写
<main>{children}</main>→ hydration 后 DOM 留下多个<main></main> - 服务端模板未做路由判断,列表页和详情页共用同一套
<main></main>包裹逻辑 - SPA 路由切换后旧
<main></main>没被移除,新内容又塞进另一个<main></main>
<main></main> 能不能嵌套在 <header></header> 或 <footer></footer> 里
不能。HTML5 规范强制禁止 <main></main> 成为 <header></header>、<footer></footer>、<nav></nav>、<aside></aside> 或另一个 <main></main> 的后代。这不是兼容性问题,而是语义冲突——主体内容不可能“属于”页眉或页脚。
立即学习“前端免费学习笔记(深入)”;
错误写法:<header><main>...</main></header> → 读屏器大概率跳过
正确结构:必须与 <header></header>、<nav></nav>、<footer></footer> 并列,直接位于 下
例外情况:<header></header> 作为 <main></main> 的**直接子元素**是允许的,但仅限于“本篇文章的标题与元信息”,不是整个网站头部。
SPA 路由中怎么安全更新 <main></main> 内容
关键原则:标签实例只存在一个,内容动态替换,而非重复创建。
推荐做法:
- 在根 Layout 组件中声明唯一
<main></main>,用<outlet></outlet>(React Router v6)或<router-view></router-view>(Vue Router)作为其唯一子节点 - 避免在
ArticlePage.tsx和ProfilePage.tsx中各自写<main></main> - 404 页面等异常状态,不要靠
<main>{error ? <notfound></notfound> : <page></page>}</main>,而应通过状态控制<main></main>节点本身的渲染与否
注意:不要用 JS 整个替换 <main></main> 元素,只替换其子节点;否则 SSR 与客户端 hydration 可能错位,导致语义丢失。
<main></main> 里到底该放什么、不该放什么
判断标准极简:**如果这块内容复制粘贴到另一个页面还能原样用,那它就不属于 <main></main>。**
✅ 应包含:
-
<article></article>(独立博客正文) -
<ol></ol>或<ul></ul>(商品列表、搜索结果流) -
<form></form>(独立提交表单,非登录框这类通用模块) -
<section></section>(如“用户评价”、“FAQ”等当前页专属子模块)
❌ 必须排除:
-
<nav></nav>(全局导航)、<footer></footer>(页脚版权) -
<aside></aside>(侧边推荐栏、广告位) - 面包屑(应使用
<nav aria-label="Breadcrumb"></nav>) - 登录框、关注按钮等跨页通用 UI
最容易被忽略的一点:<main></main> 不带任何默认样式,也不影响布局。加了却没变化?说明它干的是语义活,不是排版活——这恰恰是对的。



















