先聊个直白的问题你现在打开一个别人写的网页右键查看源代码能不能在三秒之内说出页面的整体结构头部是哪个区块、导航是哪个区块、正文从哪开始、侧边栏又是什么时候结束的。如果你的答案是不太能那十有八九是div写多了。HTML5的语义化标签说白了就是让我们别再用一堆无意义的div硬扛整个页面。不管是老手重构旧项目还是新手刚学完标签准备上手都会遇到同一个坎标签选不对、结构理不清、层级乱到飞起。这篇文章把HTML5语义化标签和文档结构这件事拆开揉碎了讲一遍从标签分类、骨架搭建到工程化实践尽量让每个人都能直接照着抄看完就能用在自己的项目里。1. 先搞清楚语义化到底在解决什么问题1.1 你写过的那些div坟场我见过太多页面整个结构大概长这样div classwrapper div classheader div classnav div classlogo.../div div classmenu.../div /div /div div classmain-content div classleft.../div div classright.../div /div div classfooter div classcopyright.../div /div /div这段代码放在浏览器里长得跟别人的页面没什么区别但问题在于你把它交给一个刚接手的人或者一个月后的自己完全看不懂哪个div是干嘛的。class名写得好还能猜个大概class名写得烂就真成标签坟场了只能顺着样式表一条条往回倒。什么叫语义化就是让标签自己说出它的含义和用途。就像你整理房间与其在三个同款白色收纳盒上贴杂物A杂物B杂物C的纸条不如直接用不同颜色、不同形状的盒子一看就知道哪个装衣服、哪个装书。HTML5推出一系列带明确含义的标签就是为了让页面结构的每一块区域不用看CSS也能被读出来。1.2 语义化的三重收益第一层收益是写给开发者的代码可读性直线上升。用header、nav、main、footer写出来的结构别人哪怕是第一次接手五分钟就能把页面脉络摸清楚。这一点在团队协作里价值极大code review的时候省下来的沟通成本非常可观。第二层收益是写给机器的搜索引擎和屏幕阅读器能更好地理解页面。搜索引擎抓取页面时会优先给main、article这类重点区域的权重让页面正文更容易被收录和索引。屏幕阅读器在给视障用户朗读页面时也可以通过landmark role直接跳转到对应的功能区块比如跳到主导航跳到正文内容没有语义化标签的话这一步是很难实现的。第三层收益是写给未来的页面结构更经得起迭代。语义化标签的边界相对明确加需求、改版时新内容往对应的区块里放就行不太会出现新模块不知道嵌套在哪个div里的纠结。当然这不是万能的但至少比一坨div之间的自由搏击要省心不少。2. HTML5语义化标签全景拆解2.1 文档级语义标签header、nav、main、aside、footer我把这些标签理解成页面的骨架件它们定义了整份文档的宏观布局。header页头放站点标题、logo、标语、搜索框这些门面内容。需要注意的是header不只可以用在页面顶部它也可以作为某个区块的头部比如一个article区块的开头部分。所以严格来说header表示的是某一块区域的头部而不是整个页面的顶部这个细节很多人会忽略。nav导航区块放主要跳转链接。一个页面通常只有一个主导航但nav标签本身是可以在页面里出现多次的比如页脚的友情链接区、文章里的“相关阅读”列表只要它们具备明显的导航属性都可以用nav包裹。main页面主内容区整份文档里只能出现一次。main和header、footer最大的区别在于它的独占性这也是判断页面结构是否健康的一个硬指标。如果代码里出现了两遍main就说明结构有问题了。aside侧边栏或附加信息区放广告、相关推荐、标签云、作者简介这类和正文有关系但又不属于主干的内容。footer页脚放版权声明、联系方式、站点地图链接等内容。和header一样footer也不是只属于页面底部文章结尾的作者署名区、附加信息栏都可以用footer。这几个标签组合起来一个典型页面的结构轮廓就出来了header页面头部/header nav主导航/nav main主内容/main aside侧边栏/aside footer页脚/footer2.2 内容级语义标签article、section、hgrouparticle表示一份独立完整的、可以被单独分发或复用的内容。一篇博客文章、一条论坛帖子、一则新闻评论、一个产品卡片都算是article。判断标准很简单把这段内容单独拿出来放在另一个上下文里它还是不是一句完整的话如果是那就该用article。section表示一个主题相关的区域通常带有自己的标题。section和article最容易搞混我自己的使用习惯是article是完整独立的内容比如一篇文章里的一个个帖子section是按主题切分的部分比如一篇文章里背景方案结果这些小节。文章本身是article文章里按主题划分的小节就是section。当然section不是必须的它强调的是内容分块而不是内容完整。hgroup这个标签的命运比较曲折。它原本用来组合一组标题比如主标题加副标题hgroup h1HTML5语义化标签实践/h1 p写给前端工程师的结构优化指南/p /hgroup但后来hgroup的语义定义被调整过现在它主要作为一个composite元素使用实际项目中用得也不多。如果你是在写博客文章需要表达主副标题直接用h1加上p包裹副标题也是一种完全可行的做法不必非要套一个hgroup。2.3 文本级语义标签mark、time、figure、address这些标签单个看好像很不起眼但在细节处理上非常见功底。mark用来高亮标记与上下文相关的文本比如搜索结果里命中关键词的部分、引用文章里需要引起注意的段落。它有浏览器默认的黄色背景你可以用CSS覆盖样式但注意不要去掉它本身的语义。time表示日期时间。HTML5在没有time标签的时候日期通常就是一串普通文本扔在页面里机器没法识别。time标签配合datetime属性可以让机器读懂准确时间比如time datetime2024-06-15六月十五日/timefigure用来包裹一张图、一组图表或一段代码示例通常配合figcaption提供说明文字。这里的要点是figure的内容应该对文档是可有可无的删掉它不影响文章的完整性但figure本身不限于图片代码块、视频、统计图表都可以放进去。address表示联系信息。注意它是给人或组织用的不是给普通地址用的比如你写一篇教程结尾放上作者邮箱就可以用address包起来。2.4 交互与状态标签details、summary、dialog这几个标签是很多前端攻城狮日常中用得比较少的但它们很有价值。details实现一个默认收起、点击展开的折叠面板不需要写任何JavaScriptdetails summary展开查看详情/summary p这里是折叠后展示的内容。/p /detailssummary是details的子元素表示折叠面板中始终可见的标题区域。dialog原生的对话框标签可以替代一部分点击弹窗的开发工作配合showModal()方法就能弹出带遮罩的模态框。不过要注意浏览器兼容性以及它在表单交互、焦点管理上的行为跟很多现成的组件库不完全一致要不要用还得结合团队的技术栈来评估。3. 从零搭建一个标准文档结构3.1 先画信息架构再写标签很多新手一上来就开写HTML这是不对的。我建议动手之前先把页面当成一张白纸从上到下列出所有需要的内容模块然后用最简单的方框把布局画出来。比如你要做一个技术博客首页可以先画出这样的结构顶部栏logo、搜索框、登录按钮主导航前端、后端、移动端、数据库内容区最新文章列表每篇文章标题、摘要、日期、标签侧边栏热门文章榜、广告位页脚版权、友情链接画完这个方框图再往每个框里填对应的语义化标签顶部是header导航是nav文章列表是main里套article列表侧边栏是aside底部是footer。一句话总结先有信息架构后有标签选择。3.2 一套可以直接抄作业的骨架代码这里给出一套我实际项目中常用的页面骨架注释已经写得很详细!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title博客首页/title /head body header classsite-header div classbranding a href/ title返回首页前端编码日志/a /div nav classprimary-nav aria-label主导航 ul lia href/htmlHTML/a/li lia href/cssCSS/a/li lia href/jsJavaScript/a/li lia href/projects项目实战/a/li /ul /nav /header main classpage-main h1最新文章/h1 article classpost-item h2a href/posts/semantic-html深入理解HTML5语义化/a/h2 p classpost-meta time datetime2024-06-156月15日/time 作者address老周/address /p p很多人写了多年页面却依然在用div堆结构……/p /article article classpost-item h2a href/posts/css-gridCSS Grid布局实战/a/h2 p classpost-meta time datetime2024-06-106月10日/time /p pGrid是现代CSS最具革命性的布局方案之一……/p /article /main aside classsidebar section classhot-list h2热门文章/h2 ol lia href#前端性能优化清单/a/li lia href#Flexbox踩坑记录/a/li /ol /section /aside footer classsite-footer p© 2024 前端编码日志/p /footer /body /html这套骨架直接复制到新项目里就能跑起来结构清晰、层级明确也能很自然地扩展后续的样式和脚本逻辑。3.3 heading层级管理H1到H6的正确使用姿势标题层级是文档结构里最容易翻车的地方没有之一。页面里应该只有一个H1用来描述页面或文章的核心主题。首页的H1通常就是站点的名字或首页的核心标题文章详情页的H1是文章标题。然后H2是主要的区块标题H3是区块下的小节标题以此类推。要注意的是标题层级不能跳着来。从H1直接跳到H3中间少了H2屏幕阅读器的大纲功能就会乱套搜索引擎理解页面结构时也会吃亏。我建议在写代码时随时检查页面大纲尤其改版的时候尽量保证标题层级是逐级递减的。还有个小技巧标题和语义化标签的关系。H2、H3这类标题可以放在article、section里面用来表明这个区块的主题。但如果你已经在header里用了H1后面的区块就别再出现H1了一个页面只有一个H1是硬性原则。3.4 表格和表单的语义化细节表格和表单在日常开发中出现频率极高但语义化往往被忽略。表格方面现代写法绝对不应该用table做页面布局了table只能用在真正的表格数据上。写数据表格时要用caption给表格一个标题用thead包表头用th标识表头单元格用scope属性告诉机器这个表头是作用于行还是列。这不仅是语义化问题更是可访问性问题对屏幕阅读器用户来说没有scope的表格就是一张混乱的数字网。表单方面每个输入项一定要和label通过for属性关联起来label forusername用户名/label input typetext idusername nameusername为什么这么写因为点击用户名这三个字时关联的输入框会自动获得焦点这个体验对鼠标用户是翻倍的便利对触摸屏用户也很友好。同时屏幕阅读器在朗读到输入框时会自动把对应的label文字一起读出来。不写label只放一个placeholder绝对是偷懒行为。4. 工程化项目里的语义化落地4.1 组件化思维下的语义化划分现在的前端项目基本都逃不开组件化开发了Vue、React、Angular这些框架里页面是用一个个组件拼出来的。于是有人会问组件化和语义化标签冲突吗答案是不仅不冲突反而配合得很好。组件本身就是语义化的一种高阶形式。比如你封装一个ArticleCard /组件它内部渲染的就是article标签里套图片、标题、摘要的结构封装一个PageHeader /内部渲染的就是header加nav。用组件划分页面区块的思路跟用语义化标签划分页面区块的思路是一致的都是让结构有名字、有边界、可复用。实际操作中我建议抽象组件的时候想一下这个组件在页面里扮演的角色是什么。如果它是一块独立的、可复用的内容内部根元素用article如果它只是一个包裹容器没有实际语义那用div也无可厚非。不要把所有组件根元素都写成div那样组件库里的每个组件就都变成白色收纳盒了。4.2 用CSS重置消除默认样式干扰有人可能会担心浏览器对语义化标签的默认样式会不会影响我的CSS布局答案是会但这个问题早就被解决了而且解决得非常成熟。不同浏览器给H1到H6、p、ul、figure、blockquote这些标签设置的默认样式并不完全一致。为了让项目在各个浏览器上的起点一致工程化项目里通常都会引入CSS reset或normalize.css。reset会把所有默认margin、padding清零normalize则尽量统一不同浏览器的默认样式。不管用哪一种目的都是先抹平差异再按照设计稿写自己的样式。这里有一个容易被忽略的点给语义化标签重新赋能样式时需要自己上心。比如normalize.css默认会处理main的display属性但如果你用的是ie时代遗留的HTML5 shiv方案就得自己写main { display: block; }否则在一些老旧浏览器里main标签的display值不是block布局会跑偏。4.3 可访问性与SEO的双重校验工程化项目上线前语义化标签做得对不对我会用两个层面的检查来把关。第一层是屏幕阅读器模拟。如果你用Chrome可以装一个屏幕阅读器插件或者直接用WAI-ARIA的一些工具来验证页面。重点听一下页面加载完后会不会直接播报好多个div有没有正确的landmark如果屏幕阅读器把navigationmaincontentinfo这些landmark识别出来了说明语义化是达标的。第二层是SEO工具检查。很多SEO分析工具会出具页面结构报告比如是否存在多个H1、是否存在缺失的alt属性、title描述是否完善等。这类报告里的结构部分基本就是围绕语义化标签和标题层级在做文章。可能有人会想项目又不做SEO语义化是不是就没用了不对可访问性层面依然有硬需求。比如一个页面上有多个导航区域时你可以用aria-label给它们区分名字nav aria-label主导航.../nav nav aria-label页脚导航.../nav这就是努力让机器理解页面的意义对残障用户来说就是在帮他们顺畅地使用互联网。4.4 与自动化测试、调试工具的配合工程化项目里语义化还能帮上自动化测试的忙。e2e测试或者组件测试里最痛点就是怎么稳定地定位一个元素。如果项目里遍地都是div加一串神秘的class类名测试脚本只能跟着CSS类名走类名一重构测试全崩。但如果你用语义化标签把页面骨架立住了测试代码里就可以相对稳定地定位到结构位置// 假设用 Playwright 做 e2e 测试 const mainContent page.locator(main); const firstArticle mainContent.locator(article).first(); const articleTitle firstArticle.locator(h2 a);这一套选择器基本不会因为样式调整、class改名而大动干戈因为语义化标签是页面结构的骨架只要页面功能没变骨架就不会变。调试的时候也一样。开着浏览器的Elements面板语义化结构的页面一拉到底导航、正文、侧栏分得清清楚楚找元素的速度不是快了一星半点。相比之下全div页面滚动起来全是灰盒子定位得靠人肉匹配层级。5. 常见问题与排查技巧实录5.1 为什么我的main标签不管用一个大坑在IE老版本里HTML5新增的语义化标签默认是inline元素display值是inline所以布局会碎掉。解决办法很简单总结一下大概是三条路。第一引入html5shiv脚本让老版本浏览器认识这些标签第二在CSS里手动把所有语义化标签设置为display: block第三也是最推荐的直接用normalize.css之类的重置样式表这个问题它会帮你处理。如果你今天还在维护老项目这个坑真的要留意。5.2 同一个页面能出现多个header吗能。很多人被页面只有一个header的直觉误导了。其实header的语义是某块区域的头部页面顶部可以有一个header每篇文章article里也可以有自己的header用来放文章的标题、日期、作者信息。footer同理文章底部也可以有自己的footer放标签、阅读量这些次要信息。页面级header和内容级header不要混为一谈关键是你得知道这个header到底在为谁带头。5.3 aria-label和title属性怎么选这两个属性经常被人拿来对比它们的作用有重叠但又不同。title属性是给所有可见元素提供额外提示的鼠标悬停时浏览器会显示一个小气泡而aria-label是给屏幕阅读器等辅助设备提供可访问名称的它在视觉上不显示任何内容。对于nav、header这类本身不带文字的容器用aria-label给它一个可访问名称是合适的而对于一个普通的超链接用title提供更多提示信息也是合理的。除非是特殊需要不要在一个元素上同时堆title和aria-label机器会优先读aria-labeltitle就会冗余。5.4 检查语义化的三个快捷手段很多人问有没有什么工具能快速检查我页面的语义化做得好不好我常用的有三个路子。第一个是浏览器开发者工具里的Accessibility Tree或者Outline视图在Chrome的DevTools里可以找到。它会以大纲形式展示当前页面的标题层级和landmark一眼就能看出有没有多个H1、标题层级是否跳跃。第二个是W3C的Nu Html Checker把页面URL贴进去或者直接粘贴HTML代码它会给出所有标签使用层面的规范建议包括标题层级、属性合法性、嵌套是否正确等。第三个是自己写一段简单脚本扫标签分布。比如在控制台里快速统计页面里div、article、section这些标签的数量如果div的数量远远碾压其他语义化标签那基本可以断定语义化还没做到位。但这种检查也只是辅助手段标签数量均衡不代表语义化一定正确关键还是看内容属性和标签含义是否匹配。5.5 一个容易被忽略的细节img的alt不算语义化标签但它极其重要严格来说alt不算语义化标签它是属性但它的作用跟语义化一脉相承。给图片写alt不只是SEO需要更是给所有无法加载图片的用户看的内容描述。写alt的时候有一个常见误区啥都往里堆关键词或者写图片1照片这种毫无意义的内容。合格的alt应该是一句能描述图片内容的话它是什么、表达了什么信息。纯装饰性的图片alt可以留空并且加上空字符串不要让屏幕阅读器读出一堆莫名其妙的文件名。6. 最后分享一点个人体会说句实在话语义化标签这件事刚入行的时候我也觉得它是老生常谈反正视觉呈现一模一样写header和写div有什么区别等到自己维护起一个几万行代码的老项目然后又被各种“这个区块在哪”“这个div到底是谁开的”折磨得欲仙欲死的时候才真正理解了语义化的价值。现在我自己写代码有一个固定的习惯每落下一个标签之前先问一句这个元素它到底是什么。是正文就写article是导航就写nav是通用容器才写div。一套代码写下来不仅页面结构清晰连带着命名class都变得顺手很多因为标签已经把层级和角色都定义好了class只需要管样式和状态即可。另外再分享一个小技巧。如果你在重构旧项目不用追求一步到位把全站标签全部替换掉。可以从核心页面开始先把包裹结构的div换成header、nav、main、aside、footer把标题层级理顺再把文章内容的根元素换成article。一次改一个页面成本可控也不会给同事留一个查不完的diff。毕竟工程化项目的核心追求从来不是用完所有新特性而是把项目维护成本降到最低。语义化标签也一样它是帮你降低成本的工具不是一个为了炫技而存在的指标。