为什么语义化
基于 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 硬撑出视觉结构。
<!-- div 汤:浏览器照样渲染,但「含义」是零 -->
<div class="header">
<span class="title">三个词</span>
<div class="nav">
<a>一个词</a>
</div>
</div><!-- 语义化:标签本身就讲清了结构 -->
<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 强调:语义元素让开发者「无需理解实际内容,就能读懂页面架构」。对比一下:
<!-- 不读内容,你能一眼看出这是页眉吗? -->
<div class="hdr">
<div class="t1">…</div>
<div class="lnks">…</div>
</div>
<!-- end hdr --><!-- 标签即文档:页眉、标题、导航,一目了然 -->
<header>
<h1>…</h1>
<nav>…</nav>
</header>语义化顺带消除了两类垃圾:一是为了给 CSS 找钩子而硬起的无意义 class(class="header" 完全可以被 <header> 取代);二是给一堆 </div> 收尾用的「<!-- end header -->」注释。结构清晰了,改起来也更不容易误伤。
「div 汤」的代价为什么总被忽视
因为它的代价几乎全是隐性的:
- 页面照常渲染,视觉上看不出任何问题;
- 不会抛任何错误、任何警告;
- 写代码的人自己(用鼠标 + 正常视力)几乎感受不到差异。
受害的是你看不见的那批用户和系统:依赖读屏的访客、抓取你页面的搜索引擎、接手这堆代码的同事。正因为「不报错、不影响自己」,div 汤才会在项目里悄悄蔓延。把语义化当成一种默认习惯,而不是「有空再优化」的可选项,是避免这笔隐性债的唯一办法。
下一步
理解了「为什么」,下一页进入「怎么搭」——用 header·nav·main·article·section·aside·footer 拼出页面骨架,并讲清每个元素的地标身份与嵌套规则:分区元素与页面骨架。