写页面写了这么多年,我见过太多人把 div 从头用到尾,一个页面下来十几个 <div> 嵌套,语义全靠注释猜。html 里真正用来划分页面区域的标签其实不少,而 section 和 div 是最容易让人搞混的一对——长得像、用起来也像,但定位完全不同。这篇文章就把这两个标签的区别、背后的文档规范、实际场景里的选型逻辑,以及我在项目里踩过的坑一次说清楚。适合刚入门的前端新手,也适合写了大半年页面但一直凭感觉选标签的朋友。
1. 先理清思路:页面区域的本质与标签演进
1.1 最初大家是怎么划分页面的
在 HTML4 时代,结构标签非常有限,body 里基本就靠 div 来划分一切区域。导航栏、侧边栏、内容区、脚部信息,全是 <div class="nav">、<div class="sidebar">、<div class="footer"> 这样堆出来的。那时候的约定俗称是“class 名即语义”,你看到 <div class="header"> 就知道它表示页头,但浏览器本身不知道,搜索引擎爬虫也不理解,它只看到一片无语义的分组盒子。
后来 CSS 框架流行起来,Bootstrap 这类工具更是把 div 嵌套推到了极致。栅格系统的每一行、每一列都是 div,有时为了一个圆角边框也要在外面包一个 div。这不是谁的错,是当时 HTML 规范里能用的“裸容器”只有 div。页面能跑,视觉也正常,但代码的可读性、可维护性以及信息传达能力都很弱。尤其是项目到了交接阶段,别人看你代码时根本不知道这个 div 到底是“文章内容”还是“装饰性容器”。
HTML5 出现后,规范增加了一整套语义化标签:header、footer、nav、main、article、aside、section。它们存在的主要目的不是代替 div,而是把“这个区域在页面里是什么角色”这件事告诉浏览器、搜索引擎和辅助设备。用一句话概括:div 是给你做样式布局用的,section 是给页面内容定义主题结构用的。
1.2 section 和 div 在规范里的定义
先看 W3C 对 div 的定义:div 是一个无语义的通用容器,本身不表达任何内容含义。它适用于那些“找不到更合适的语义标签、但确实需要将元素分组”的场景。比如一个 flex 布局里的包裹层、一个为了让 JavaScript 统一操作的容器、一个没有任何标题性的装饰块,这些都是 div 的合理用途。
section 的定义则明确一点:它表示文档中的一个独立区域,这个区域的内容在主题上是一个整体,通常包含一个标题(h1-h6 中的一个)。规范原文用了 “thematic grouping of content” 这个说法,意思是“有主题的内容分组”。也就是说,section 适用的场景是内容之间确实存在“章节”或“区块”的归属关系,比如文章里的多个分节、产品页里的“规格参数”“用户评价”“相关推荐”这种各自成块的区域。
我见过一个比较形象的类比,可以用书来理解:div 相当于书页上的一个透明塑料袋,你可以在里面放任何东西,纯粹是为了收纳和整理;section 相当于书里的章,每章有自己独立的主题,章内内容围绕这个主题展开,章与章之间是并列或者递进的关系,并且正常情况下每一章都要有章名。
1.3 为什么我不建议你“统一用 div”
有些开发者嫌语义化标签麻烦,觉得反正渲染效果一样,全用 div 也不会出问题。这话在视觉效果上没错,但在另外几个维度上会吃亏。
第一是代码可读性。纯 div 页面里,你得靠 class 名和注释来推断结构,而用 section、article、header 这些标签之后,即使不看 class,扫一眼标签就能知道页面骨架。尤其项目里使用 Vue、React 这类组件化框架时,组件层级一多,div 嵌套深了之后,VSCode 的标签匹配都困难,光标一跳动你根本分不清当前括号属于哪一层。这个痛点后面单开一节细说。
第二是搜索引擎和信息消费方理解成本。爬虫在分析页面时,虽然主要靠正文和链接,但结构语义能帮助它对页面内容做更细粒度的分区。一个自然的 section 结构,比一堆 div 更容易让搜索引擎判断“这里是正文章节”还是“这里是侧边栏”。
第三是辅助技术。屏幕阅读器在浏览网页时,会读出一个区域一个区域的结构。使用语义化标签,可以让读屏用户直接通过快捷键跳转到 main、nav、section 等区域,不需要从一堆 div 里摸索。这在无障碍审计里是实打实的加分项。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. section 与 div 的核心区别:语义、大纲与可访问性
2.1 语义标签到底给谁看
很多人问:section 和 div 在浏览器里渲染出来都是块级元素,默认样式也没多大差别,那语义是给谁看的?
答案是给三类“读者”看的:浏览器扩展和辅助工具、搜索引擎爬虫、团队里的其他开发者。浏览器内核本身对语义标签有内置映射,比如 <article>、<aside>、<nav> 会参与可访问性树的构建,读屏软件可以调用这些映射来提供快速导航。搜索引擎的爬虫也有一套对结构化标签的解析规则,虽然各大搜索引擎对语义标签的权重加成态度很谨慎,但更清晰的文档结构有助于把握页面的主题层次。
所以,如果你写的页面只是给自己看、不打算被搜索、也不考虑可访问性,那 div 确实够用。但只要你做的是面向真实用户的 Web 产品,语义化标签就不是“加分项”,而是“基本素质”。
这里要纠正一个常见的误解:section 本身并不会让文字变大、让背景变色。它的默认样式和 div 在绝大多数浏览器里完全一致,区别只在 DOM 语义层面。如果你在浏览器开发者工具里只看计算样式,根本看不出差别。所以别指望用了 section 就自动有样式,样式还是要靠 CSS 去写。
2.2 文档大纲算法和常见的误解
HTML5 规范早期提出了一个“文档大纲算法”(document outline),它的思路是:通过 h1-h6 和 section 的嵌套关系,自动生成页面的大纲结构。理论上,你可以写多个 h1,每个放在不同的 section 里,大纲算法会把这些 section 视为并列的顶层章节,从而避免传统模型里多个 h1 导致的混乱。
但实际情况非常尴尬:这个算法从提出到现在,没有任何主流浏览器完整实现过。你在 Chrome 里打开一个页面,文档大纲不会可视化地给你展示出来;屏幕阅读器的主流组合也对 outline 的支持非常有限。也就是说,网上有些文章吹“section 可以解决多 h1 问题”,这个观点在规范层面有道理,但在实际浏览器行为里并不成立。
所以在实际项目中,我建议还是遵循传统做法:一个页面尽量只有一个能体现整体主题的标题层级,h1 优先给页面的主标题,section 内部优先使用 h2-h3 来组织小节。section 的优势在于“让结构更清晰、代码更可读”,而不是“自动生成完美大纲”。别把规范里还没有落地的东西当成实际收益去依赖。
这个知识点很多人会忽略,尤其是面试时容易被问倒。我的经验是:当面试官问“section 和 div 的区别”时,你如果能说出“文档大纲算法理论上支持 section 分区,但实际没有浏览器实现”,比单纯背出“section 是语义标签,div 是无语义标签”要加分得多。
2.3 可访问性:屏幕阅读器与 ARIA landmark
语义化标签对无障碍支持的贡献,比很多人想象中大。ARIA 规范定义了一组 landmark 区域,传统上是用 role="banner"、role="navigation"、role="main"、role="contentinfo" 这些属性来标记页面区块。HTML5 的很多语义标签其实内置了对应的 landmark 角色,比如:
<header>一般映射为 banner(但只在作为 body 直接子元素时)<nav>映射为 navigation<main>映射为 main<footer>一般映射为 contentinfo(同样仅在作为 body 直接子元素时)
但 section 本身并没有内置一个固定的 landmark 角色。它默认的 ARIA 角色是 region,而 region 这个角色只在有可访问名称(accessible name)时才被识别为 landmark。什么叫可访问名称?简单说,就是通过 aria-labelledby 或 aria-label 给这个 section 提供一个名字。
举个例子:
html复制<section aria-labelledby="specs-title">
<h2 id="specs-title">技术规格</h2>
<ul>
<li>内存:16GB</li>
<li>存储:512GB</li>
</ul>
</section>
这样,读屏用户就可以通过 landmark 导航直接跳到“技术规格”这个区域。如果没有 aria-labelledby,也没有标题,这个 section 在辅助技术眼中就只是一个普通区域,和 div 的区别变得非常有限。
反之,如果某个区块是装饰性的、或者只是用来撑布局的,那就不应该用 section,应该用 div,甚至可以直接用空 div 搭配 aria-hidden 来避免读屏反复播报无用信息。可访问性不是越多标签越好,而是每个标签的位置和用途都要正确。
3. 实战选型:什么时候用 section,什么时候用 div
3.1 判断原则:内容是否构成独立主题
我给自己定了一个判断标准,写代码时基本不用犹豫:这个区域里的内容,是否围绕一个可被命名的主题组织?如果有,用 section;如果没有,只是样式或脚本需要,用 div。
举几个例子。
页面里有一段“用户评价”区域,它有一个标题“用户评价”,里面是若干条评价内容。这个区域就是一个很典型的 section 场景,因为它有独立的主题,也有标题,可以自然成为页面里一个章节。
一个商品列表页,外面包了一个 flex 容器用于控制卡片排列的间距和对齐,这个容器本身没有主题名称,它的存在只是为了布局。这个容器就应该用 div,而不是 section。很多新人就是在这里翻车:商品卡片用 article 是合理的,但商品卡片外面的那层网格容器,不应该用 section,因为“商品列表的布局容器”不是一个内容主题,而是一种视觉组织方式。
下面是我比较常用的一个博客文章页结构示例,可以对照着看:
html复制<body>
<header>
<nav>
<a href="/">首页</a>
<a href="/archive">归档</a>
</nav>
</header>
<main>
<article>
<h1>用 CSS Grid 搭建响应式布局</h1>
<p>发布日期:2025-03-01</p>
<section>
<h2>为什么选择 Grid</h2>
<p>Flexbox 适合一维布局,Grid 更适合二维布局……</p>
</section>
<section>
<h2>核心属性详解</h2>
<p>grid-template-columns 是最常用的属性……</p>
</section>
</article>
<aside>
<section>
<h2>相关文章</h2>
<ul>
<li><a href="/post/css-flex">Flexbox 入门</a></li>
<li><a href="/post/css-grid">Grid 进阶</a></li>
</ul>
</section>
</aside>
</main>
<footer>
<p>© 2025 My Blog</p>
</footer>
</body>
这里 article 内部的两部分,每个都有 h2 标题,内容主题彼此独立,用 section 非常合适。aside 里的“相关文章”也是一个有主题的独立区块,同样可以套 section。而如果只是需要在页面里把两列内容框进一个圆角边框、或者给它们加一层 flex 布局,那直接用 div 就好。
3.2 典型场景拆解:列表、侧边栏和装饰层
分场景来讨论选型会更有效。
场景一:文章正文的多级章节。 这个最没有争议,正文里每一章、每一节都用 section 包裹,章内标题用 h2 或 h3,语义清晰。对于长文档,section 嵌套 section 也是允许的:外层是一个大的主题,内层是它下属的子主题。但这里有个隐性问题——section 嵌套太深,会让视觉上结构复杂,实际阅读时也容易迷失。我见过一个项目里 section 套了四层,每层都有标题,人眼根本分不清层级,读屏用户的大纲也是一塌糊涂。所以我的建议是:层级尽量控制在两层以内,再多就要考虑是不是拆成独立页面更合理。
场景二:页面的侧边栏区域。 侧边栏通常包含多个模块:搜索框、热门文章、标签云、广告位。每个模块内容独立、有标题的,用 section;没有标题、纯粹为了放广告位或者撑样式的,用 div。这里我要特别强调广告位的问题——广告位是最容易被误用 section 的地方。广告位一般不需要被搜索引擎和读屏工具当作正文章节理解,用 div 加上对应的标识属性更合适。如果广告位有明确的主题名称(比如“赞助商推荐”),那也可以考虑 section,但前提是标题真实存在。
场景三:组件库的通用容器。 如果你在维护一套组件库,按钮组件、卡片组件、弹窗组件内部可能都有包裹层。这些内部包裹层强烈建议用 div,不要用 section。原因是组件内部的结构往往需要复用,而 section 的语义会暗示“这是一个独立章节”,在多处复用时会污染整个页面的语义结构。组件库里比较常见的做法是:组件根元素用自定义数据属性或者类名标识,内部布局层用 div,只有那些真正承载内容区块的组件(比如 Accordion 的每个折叠面板、Tab 的每个标签页)才用 section,而且每个面板都要有可访问名称。
3.3 嵌套关系与禁区
section 和 div 的嵌套关系非常灵活,section 里可以套 div,div 里也可以套 section。但有几个禁区要特别注意。
第一个禁区:不要把 section 当作 content 包装器。有人习惯把整个页面的内容区写成 <section class="content">,里面再塞 header、footer、侧边栏等。这是错的。page-level 的布局应该交给 body 的子元素和 main、aside 等标签来组织,section 不该承担“整个页面主体容器”的职责。你应该把 section 用在主体内容内部,去划分更细粒度的章节。
第二个禁区:不要让 section 没有标题。规范本身要求 section 通常包含标题。实践中的底线是:如果一个 section 没有标题,那么它和 div 相比没有任何优势,反而会让开发者产生误解。业界有一条经验规则:想用 section 的时候,先确定这个区域能不能写一个真实的标题,如果写不出来,大概率应该用 div。
第三个禁区:不要用 section 做纯视觉装饰。背景色、圆角、阴影、间距,这些都属于视觉呈现层面。比如页面里一个红色背景的促销横幅,只有一个按钮,没有标题,它用 div 就够了。即便它视觉上非常像一个“区块”,但没有内容主题的区块永远不是 section 的菜。
第四个禁区:不要在 a 标签、button、li 这些内部随意套 section。这些元素有更具体的语义,section 塞进去会把原本清晰的语义搞乱。比如一个列表项里面,如果只有简单的标题和描述,直接用 li 本身的结构就行,不需要再包 section。除非列表项内部内容足够长、且有多个子主题,才考虑 section,但这种情况非常少见。
4. 实操中的问题与排查经验
4.1 VSCode 里 div 太多分不清怎么办
这个是我经常被问到的问题,尤其是在团队项目里,几百行 JSX 模板或者 HTML 文件里全是 div,VSCode 里看代码跟看俄罗斯方块一样。我总结了几条实际可用的经验。
第一,优先优化标签结构。把能换成语义化标签的 div 换掉,减少裸 div 的数量,这样 VSCode 的结构折叠、括号匹配从视觉上就清晰很多。section、article、header、footer、aside 这些标签在代码里起到“路标”作用,扫一眼就能定位到对应区域。
第二,利用 VSCode 的标签匹配功能。光标放在某个 div 的开头或结尾,按 Cmd+Shift+P(macOS)或 Ctrl+Shift+P(Windows)打开命令面板,执行“转到括号”,就能跳到配对的标签位置。另外,最好把设置里的 editor.matchBrackets 设为 always,并在颜色主题里开启彩虹括号插件,这样嵌套层级会显示不同颜色。
第三,善用折叠。VSCode 默认支持代码折叠,把鼠标移到行号附近,会出现折叠箭头。如果你想直接折叠整个 div 区块,可以先把光标放到 <div> 那一行的任意位置,然后按 Cmd+K Cmd+0 折叠所有层级。这样代码缩略图会清晰得多。如果你用了 EditorConfig 和 Prettier,统一格式化之后,各层级的缩进对齐也会更直观,减少视觉混乱。
第四,如果项目里已经用了 Vue 或 React,建议在组件拆分时尽量让每个组件控制一两个语义区块,而不是把一长串 div 全部写在一个模板文件里。组件本身就是一种结构封装,配合语义标签,代码会好读很多。
4.2 常见报错和校验问题的识别
很多朋友看到 section 相关报错,会以为是用错了标签,实际上有些报错根本不是 HTML 层面的问题。
比如你搜索热词时会看到一条类似 “error: L6985E: unable to automatically place at section system_py32f0xx.o(.a” 的信息,这是一条嵌入式链接脚本的报错,跟网页里的 <section> 标签没有任何关系。它出现在 ARM 工具链或者链接器里,意思是某个目标文件的段(section)无法自动放置到指定内存区域。这里你只需要记住:HTML 里的 section 是内容分区标签,链接脚本里的 section 是代码段、数据段这类概念,发音一样,但完全是两个世界。 如果你在编译单片机程序时遇到 L6985E,应该去查链接脚本的段分配,而不是网页代码。
真正和 HTML section 相关的常见提示有两类。第一类是 W3C 校验器给出的警告:“Element section must not be empty”或者“Section lacks heading”。原因通常是 section 里没有标题内容。解决办法很直接:要么给 section 补一个真实可见的标题,要么把标签改成 div。第二类是 ESLint 配合 jsx-a11y 插件时,可能会提示 “aria-role should be valid”或者 “non-interactive element has incorrect role”,常见原因是 section 在没有标题的情况下被设置成 role="region",但缺乏 aria-label。解决方法是补上 aria-label,例如 <section role="region" aria-label="热门产品">。
在项目里如果遇到“html 文件无法预览”的问题,先看是不是 VSCode 没有安装 Live Server 或者 Open in Browser 这类插件。右键 HTML 文件,选择“Open with Live Server”就能起一个本地静态服务,页面里如果引用了相对路径的 CSS、JS 文件,用文件协议直接双击打开有时会因为跨域问题而加载失败,这也是新手比较常踩的坑。无论文件是否含有 section、div,只要资源加载路径不对,预览都会异常。
4.3 给新手的一条自查清单
写代码前,先拿这个清单过一遍:
- 这个区域的内容是否能提炼为一个主题?
- 这个区域是否需要一个标题?(h2 或 h3,不是装饰性文字)
- 这个区域是否会被读屏用户作为独立区块快速跳转?
- 这里的内容是否属于文章、文档或有主题信息的排列?
如果上面几条里有任何一条是“是”,优先考虑 section。如果都不沾,只是单纯为了布局或脚本分组,用 div。
再补一条判断辅助:当你犹豫该用 article 还是 section 时,想想内容是否能独立发布、独立分发。能独立成篇的用 article,依赖上下文才能成立的章节用 section。比如一篇新闻稿的正文属于 article,新闻稿里的“背景资料”小节属于 section。这个区分思路也经常和 div 混淆一起考,我建议把 article、aside、section、div 四者的关系放在一起理解:
- article:独立完整的作品
- section:作品内的章节
- aside:与内容相关但可分离的补充
- div:无任何语义的分组容器
5. 我在实际项目里沉淀下来的选型原则
最后分享几个我写代码时的个人习惯,不一定适用所有团队,但对我带的项目确实有效。
第一,写一个新的页面结构时,我用“语义画图法”:先不写代码,把页面的结构树画出来,标出哪个区域有标题、哪个区域是独立主题、哪个区域只是布局容器。这个过程五分钟都用不了,却能避免之后一半以上的返工。结构树画完,section 和 div 的分布自然就出来了。
第二,我要求 components 内部默认用 div,页面级路由组件里才出现 section 和 article。这样做的好处很实在:组件库的复用性和页面结构的可读性都能兼顾。组件内部如果需要语义,通过 props 或者具名插槽暴露给使用方,而不是在组件内部写死 section。
第三,我习惯给每个 section 配一个标题,哪怕这个标题在视觉上被隐藏起来(用 CSS 的 visually-hidden 类)。这样既能保证读屏用户的使用体验,也能强制自己思考“这个 region 的主题到底是什么”。
第四,不要在 CSS 里给 section 和 div 定义不同的默认样式。section 应该和 div 一样是“无色”的,它只负责结构语义,视觉样式统一通过 class 去控制。如果你给所有 section 加边框或背景,那等于把语义标签和视觉样式强绑定,后续布局调整会很痛苦。
这个内容后续还可以这样扩展:如果你想把页面语义化做到极致,可以继续了解 RDFa、Microdata 和 JSON-LD,它们能在语义标签之上进一步给搜索引擎提供结构化数据。但那是另一个进阶话题了。先把 section 和 div 的关系理清楚,日常工作里 80% 的标签选型问题就已经解决了。
