标签是页面唯一主内容语义锚点,必须且只能出现一次,用于屏幕阅读器快捷跳转和搜索引擎主题提取;重复或嵌套会导致辅助技术行为不可预测、lighthouse报错、seo摘要错乱。

main 标签本身已自动携带 role="main",手动再加这个属性不仅没用,反而可能干扰屏幕阅读器——比如旧版 JAWS 会重复播报“主内容开始”,用户听到两次提示却只看到一个区域。
为什么能被快速识别为正文锚点
屏幕阅读器(NVDA、VoiceOver)把 main 当作“页面主体内容”的唯一可信入口。它不靠猜测,而是直接读取无障碍树中 role 值为 main 的元素。这个角色是浏览器在解析 main 标签时静态注入的,无需 JS、不依赖 CSS,开箱即用。
- 缺失
main时,读屏器退而求其次:先找role="main",再找不到就从第一个h1开始算正文——但页眉里常有h1,结果用户得手动下拉 7–12 次才摸到真正要读的内容 -
main不是视觉容器,它不撑高度、不设 margin、不触发 BFC;它的作用纯粹是语义锚点,告诉辅助技术:“这里开始,才是用户该聚焦的核心” - 验证是否生效:Chrome DevTools → Accessibility 面板 → 点击该元素,Computed Role 应显示
main,而不是generic
多个会导致 landmark 结构崩溃
WCAG 明确要求每页仅一个 main。SPA 中常见错误是每个路由组件都渲染独立的 main,hydration 后 DOM 中残留多个实例。结果就是 NVDA/VoiceOver 在按 M 键跳转时行为不可预测:可能随机选中、跳过全部、或反复播报。
- React Router v6 正确写法:
<main><outlet></outlet></main>放在根 Layout 组件里,子组件绝对不带main - Vue Router 同理:只在
<router-view></router-view>外层套一次main - 服务端模板需做条件判断,避免列表页和详情页共用同一段包裹逻辑
main 里混入非主内容会污染语义边界
main 定义的是“可独立分发的内容主体”,不是“视觉上最显眼的区域”。把 nav、footer、侧边栏甚至全局 header 塞进去,等于告诉屏幕阅读器“这些也是主内容”,直接破坏 landmark 导航路径。
- ✅ 允许:
article、纯文本段落、独立表单、商品列表 - ❌ 禁止:
nav、footer、网站级header、广告位、弹窗 - ⚠️ 注意:作为文章标题区出现在
main内的header是合法的,但必须是内容级header,不能是全局页头
最容易被忽略的一点:main 没有默认样式,也不影响布局流——它纯粹是语义锚点。很多人试图靠它“撑开高度”或“统一外边距”,结果发现它既不触发 BFC,也不自带 margin/padding,纯属白忙活。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











