Skip to content

为什么语义化

基于 HTML Living Standard · 核于 2026-06

速查

  • 语义化 = 按含义选元素<button>/<nav>/<article>),非语义 = 按外观堆 <div>/<span>
  • 可访问性:浏览器据语义构建无障碍树(AOM),屏幕阅读器靠地标和标题导航;<div> 汤里这些全是空的
  • 内置行为:<button> 自动进 Tab 序、响应 Enter/Space;<div role="button"> 这些全得自己补,还容易漏
  • SEO:搜索引擎用 <main>/标题层级理解页面主次,语义结构利于收录与摘要
  • 可维护性:不读内容、只看标签就能看懂骨架;省去无意义的 class 和「</div><!-- end xxx -->」注释
  • 「div 汤」的代价是隐性的——页面照常渲染、不报错,受害的是读屏用户、SEO 和接手代码的人
  • 反模式:<p role="button"> 拿到了角色却丢了原生功能;能用原生元素就别手动贴 role
  • 红线:role 只在没有合适原生元素时才用;给 <button>role="button" 是多余的

语义化到底是什么

「语义」就是「关于含义」。语义化 HTML 的定义,web.dev 写得很直白:

「按每个元素的含义、而不是它的外观来组织内容。」

换句话说,HTML 负责「这是什么」,CSS 负责「它长什么样」。一段导航就用 <nav>,一个按钮就用 <button>,一篇文章就用 <article>——哪怕它们最终都被 CSS 改成别的样子,标签里记录的「含义」不会变。

反面教材是「div 汤」(div soup):整页除了 <div><span> 几乎没有别的元素,靠 class 名和 CSS 硬撑出视觉结构。

html
<!-- div 汤:浏览器照样渲染,但「含义」是零 -->
<div class="header">
  <span class="title">三个词</span>
  <div class="nav">
    <a>一个词</a>
  </div>
</div>
html
<!-- 语义化:标签本身就讲清了结构 -->
<header>
  <h1>三个词</h1>
  <nav>
    <a>一个词</a>
  </nav>
</header>

两段在屏幕上可以做到一模一样,但下面三笔账,决定了它们的天壤之别。

第一笔账:可访问性

这是语义化最硬的理由。浏览器在解析 HTML 时,除了 DOM,还会构建一棵无障碍树(Accessibility Object Model,AOM)——web.dev 形容它是「DOM 的语义版本」。屏幕阅读器、语音控制等辅助技术读的就是这棵树。

语义元素带来两样东西:

地标与标题导航

<header><nav><main><footer> 等会在无障碍树里登记成地标(landmark)。读屏用户可以用快捷键在地标之间跳转——「跳到主内容」「跳到导航」,秒级定位。<h1><h6> 则生成一份标题大纲,用户能像看目录一样逐级浏览全页。

而在 div 汤里,这些地标和标题统统不存在。读屏用户只能从头到尾一行行听,毫无结构可言。

隐式角色与内置行为

每个语义元素都带一个 ARIA 规范定义的隐式角色(implicit role),同时附赠原生交互行为。最经典的例子是 <button>

  • 自动加入文档的 Tab 键顺序,键盘可聚焦;
  • 自动响应 Enter 与 Space 键触发;
  • 自带 button 角色,读屏会告诉用户「这是个按钮,可以激活」。

这些全是免费的。一旦改用 <div> 模拟按钮,上面每一条都得自己用 JS / tabindex / ARIA 补回来,而且极易漏掉某一项(比如忘了 Space 键、忘了 tabindex),留下无障碍坑。

不要用 role 替代原生元素

你确实可以写 <p role="button">点我</p> 给段落贴上按钮角色。但这只补上了「角色」这层语义,原生功能一样都不会来——不可聚焦、不响应键盘。web.dev 的结论很干脆:「直接用 <button> 容易太多了。

反过来,给真正的 <button> 再加 role="button"多余的——它本来就有这个角色。role 属性只在「实在没有合适原生元素」时才登场。

第二笔账:SEO

搜索引擎爬虫和读屏面对的是同一个问题:在不理解内容的前提下,看懂页面结构。语义化直接帮了它们:

  • <main> 标出页面主体,<aside> / <nav> / <footer> 标出辅助区,爬虫能分清主次,不会把侧栏广告当成正文;
  • <h1><h6> 的层级是搜索引擎理解「这页讲什么、分几块」的重要线索;
  • <article> 这类自包含单元,利于内容被正确识别、聚合与生成摘要。

需要泼一盆冷水:语义标签不是直接的排名魔法(Google 不会因为你用了 <article> 就给你加分)。但它让爬虫更容易正确理解你的内容,这种「易于理解」长期会反映在收录质量和搜索表现上。和 <title>description 这些显式 SEO 元数据是互补关系。

第三笔账:可维护性

这笔账受益的是写代码的人——包括三个月后的你自己。

web.dev 强调:语义元素让开发者「无需理解实际内容,就能读懂页面架构」。对比一下:

html
<!-- 不读内容,你能一眼看出这是页眉吗? -->
<div class="hdr">
  <div class="t1">…</div>
  <div class="lnks">…</div>
</div>
<!-- end hdr -->
html
<!-- 标签即文档:页眉、标题、导航,一目了然 -->
<header>
  <h1>…</h1>
  <nav>…</nav>
</header>

语义化顺带消除了两类垃圾:一是为了给 CSS 找钩子而硬起的无意义 class(class="header" 完全可以被 <header> 取代);二是给一堆 </div> 收尾用的「<!-- end header -->」注释。结构清晰了,改起来也更不容易误伤。

「div 汤」的代价为什么总被忽视

因为它的代价几乎全是隐性的

  • 页面照常渲染,视觉上看不出任何问题;
  • 不会抛任何错误、任何警告;
  • 写代码的人自己(用鼠标 + 正常视力)几乎感受不到差异。

受害的是你看不见的那批用户和系统:依赖读屏的访客、抓取你页面的搜索引擎、接手这堆代码的同事。正因为「不报错、不影响自己」,div 汤才会在项目里悄悄蔓延。把语义化当成一种默认习惯,而不是「有空再优化」的可选项,是避免这笔隐性债的唯一办法。

下一步

理解了「为什么」,下一页进入「怎么搭」——用 header·nav·main·article·section·aside·footer 拼出页面骨架,并讲清每个元素的地标身份与嵌套规则:分区元素与页面骨架