先做一个快速检测:打开你最近开发的一个页面,按下 Ctrl+F 搜索 。如果搜索结果里出现2个以上,那这篇文章建议你认真读完。
本期要聊的主题,是HTML标签中一个看似简单、实际极易踩坑的核心知识点:main标签的唯一性。很多开发者知道这个标签的存在,但真正写到项目里,尤其是用了React、Vue这类框架后,稍不注意就会翻车。

为什么main标签必须且只能出现一次
先建立一条关键认知: 从来不是一个单纯的布局容器,它的本质是语义灯塔。
具体什么意思呢?
屏幕阅读器用户(例如使用NVDA、VoiceOver的视障用户)会通过按下"M"键快速跳转到页面的主要内容区域,这是他们浏览网页最核心的快捷键之一。搜索引擎爬虫在提取页面主题时,也会将 里的内容作为优先分析对象。如果页面上出现了两个 ,辅助技术的表现就会完全不可预测——可能会跳过所有、随机选中一个,甚至反复向用户播报“主内容开始”。
从检测工具的角度看,Lighthouse 会直接报 duplicate-main 错误,axe 工具也会标红警告。WCAG 2.1 更是明确写进了规范:每页必须且只能有一个main地标(landmark)。浏览器DevTools虽然不一定给出明确的警告,但错误依然存在。
再次强调:这不仅是“最佳实践”,而是规范级别的硬性要求。
哪些写法会导致main重复或嵌套错误
掌握规则后,再看实际开发中容易在哪几个地方“翻车”。主要集中在SPA应用和服务端模板场景:
- React / Vue 的路由组件里各自加了一个 :每个路由组件都写上了
,到页面水合(hydration)后,DOM里留下了多个实例。这是目前最常见的问题。{children} - 布局文件与页面文件双层包裹:以 Next.js 为例,在
app/layout.tsx里提前塞了一个,等到了具体页面的app/page.tsx,又包了一层。双倍地雷。 - 模板引擎不做路由判断:使用 EJS 或 Twig 这类模板时,列表页和详情页共用同一套包裹逻辑,导致出现嵌套或并列的情况。
- SPA 路由切换未卸载旧节点:旧页面的
没有销毁,新内容又在另一个挂载点生成,残留节点被忽略。 - 嵌套在错误的父级下:错误写法例如
或。规范已明确禁止作为、、的后代。
这些都属于非常具体且高发的场景,希望在座的各位没有全中。
如何验证页面只有一个main标签
最简单的办法,不是去看浏览器报不报错。它大概率不会报错,直到你上线后被无障碍审计工具抓个正着。
最保险的方式是手动逐一验证:
- 打开开发者工具(F12),进入 Elements 面板。
- 按
Ctrl+F(Windows)或Cmd+F(macOS)搜索。 - 查看匹配结果。正常情况下应该仅且只有1对
标签。注意检查闭合标签是否成对,有没有被注释包裹导致页面不渲染。 - 对于SPA场景,建议特别留意:切换路由后,刷新 Elements 面板,确认旧节点是否真的被销毁。
- 如果一直在用 Lighthouse 做性能审计,可以关注它报告里的“Document has more than one main landmark”这一条。出现这条提示,说明问题已经比较明显。
main标签里该放什么、不该放什么
判断标准其实非常直观。这里给出一个容易操作的方法:
如果这一段内容,你复制粘贴到另一个页面还能原样使用,那它就不该出现在 里。
对这个逻辑做进一步展开:
- ✅ 应该放进去的:当前 URL 对应的唯一核心内容。比如文章正文、商品详情页的主体区块、用户填写表单的核心区域。
- ❌ 绝对不要放的:全站复用的模块。Logo加导航、面包屑导航、页脚的版权信息、右侧推荐栏、登录框。这些东西每个页面都可能出现,但它们不属于当前 URL 的专属内容。
- ⚠️ 一个需要特别注意的例外:
其实可以作为的直接子元素,但仅限于当前页面独有的标题。例如,文章页里的标题加发布时间。这个不能是网站全局的头部。 - ⚠️ 多区块页面的处理方式:首页可能会包含推荐区、热榜区、最新动态等多个板块。正确的做法是,用一个
包裹所有这些内容,内部再用等元素划分。不要给每个区块单独加一个。
总结一下:真正有挑战的,从来不是把标签写对。而是在每一次新增一个模块的时候,下意识地问自己一句:“这个东西,换到另一个页面还能直接粘贴过去用吗?”
如果答案是“能”,那就把它从 里移走。别犹豫。
