Skip to content

HTML 语义化与新特性

作者:青见春山
发表于:2026-07-29
字数统计:7000 字
预计阅读24分钟

涵盖 HTML 语义化标签对比、HTML5 新特性、H5 移动端适配、viewport、Web Components、Shadow DOM、跨组件通信、自定义元素生命周期、slot 插槽、template 模板与原生组件库。

一、HTML 语义化

1. 为什么要语义化?

HTML
<!-- ❌ 不语义化:用无语义的 div 堆砌 -->
<div class="header">
  <div class="nav">...</div>
</div>
<div class="main">
  <div class="article">
    <div class="title">标题</div>
    <div class="content">内容</div>
  </div>
</div>

<!-- ✅ 语义化:用有意义的标签 -->
<header>
  <nav>...</nav>
</header>
<main>
  <article>
    <h1>标题</h1>
    <p>内容</p>
  </article>
</main>

语义化优点

  1. 可读性:代码结构清晰,开发者一眼能看明白页面结构
  2. 可维护性:样式、行为解耦,更易于维护
  3. SEO 友好:搜索引擎爬虫依赖语义化标签理解页面结构
  4. 可访问性:屏幕阅读器等辅助技术依赖语义化标签
  5. 团队协作:统一的语义约定便于沟通

2. 常用语义化标签

标签用途替代方案
<header>页头 / 区块头部<div class="header">
<footer>页脚 / 区块底部<div class="footer">
<nav>导航链接<div class="nav">
<main>主内容(每个页面唯一)<div class="main">
<article>独立文章内容<div class="article">
<section>主题性区块<div class="section">
<aside>侧边栏 / 注释<div class="sidebar">
<figure> / <figcaption>图片与说明<div class="img-wrapper">
<time>时间<span class="time">
<mark>高亮文本<span class="highlight">
<address>联系信息<div class="address">

3. 关键规则

HTML
<!-- 每个页面只一个 <main> -->
<main>
  <!-- <article> 是独立可分发的内容(博客文章、新闻、帖子) -->
  <article>
    <header>
      <h1>文章标题</h1>
      <time datetime="2024-05-15">2024年5月15日</time>
    </header>
    <p>文章内容...</p>
    <figure>
      <img src="..." alt="..." />
      <figcaption>图片说明</figcaption>
    </figure>
  </article>

  <!-- <section> 是主题性分组 -->
  <section>
    <h2>相关推荐</h2>
    <article>...</article>
  </section>
</main>

<aside>
  <!-- 侧边栏 -->
</aside>

<footer>
  <address>联系邮箱:xxx@example.com</address>
</footer>

注意

  • <section> 一定要有标题(h1~h6),否则考虑用 <div>
  • <article><section> 独立性更强:article 脱离上下文仍能完整表达含义
  • 不要用 <section> 替代 <div> 当作普通容器

二、HTML5 新特性

1. 新增语义化标签

HTML
<!-- 结构相关 -->
<header>, <footer>, <nav>, <main>, <article>, <section>, <aside>, <figure>, <figcaption>, <time>, <mark>, <address>, <details>, <summary>, <dialog>

<!-- 文本相关 -->
<ruby>, <rt>, <rp>  <!-- 注音 -->

<!-- 媒体 -->
<audio>, <video>, <source>, <track>, <picture>

<!-- 交互 -->
<canvas>, <details>, <dialog>

<!-- 表单 -->
<input type="date|email|number|range|search|tel|url|color|file">

<!-- 其他 -->
<output>, <progress>, <meter>, <template>, <slot>

2. 表单新特性

HTML
<!-- placeholder:占位符 -->
<input type="text" placeholder="请输入用户名" />

<!-- required:必填 -->
<input type="text" required />

<!-- pattern:正则验证 -->
<input type="text" pattern="[A-Za-z]{3,}" />

<!-- type 新类型 -->
<input type="email" />
<input type="url" />
<input type="number" min="1" max="100" step="1" />
<input type="range" min="0" max="100" />
<input type="date" />
<input type="color" />

<!-- autocomplete -->
<input type="text" autocomplete="username" />

<!-- autofocus -->
<input type="text" autofocus />

<!-- datalist:下拉建议 -->
<input type="text" list="browsers" />
<datalist id="browsers">
  <option value="Chrome" />
  <option value="Firefox" />
  <option value="Safari" />
</datalist>

3. 多媒体

HTML
<!-- audio -->
<audio controls>
  <source src="audio.mp3" type="audio/mpeg" />
  <source src="audio.ogg" type="audio/ogg" />
  您的浏览器不支持 audio 标签。
</audio>

<!-- video -->
<video controls width="640" height="360" poster="poster.jpg">
  <source src="video.mp4" type="video/mp4" />
  <source src="video.webm" type="video/webm" />
  <track src="captions.vtt" kind="subtitles" srclang="zh" label="中文" />
  您的浏览器不支持 video 标签。
</video>

<!-- picture:响应式图片 -->
<picture>
  <source media="(max-width: 600px)" srcset="small.jpg" />
  <source media="(max-width: 1200px)" srcset="medium.jpg" />
  <img src="large.jpg" alt="..." />
</picture>

4. Canvas 与 SVG

HTML
<!-- Canvas:位图 -->
<canvas id="myCanvas" width="200" height="100"></canvas>

<!-- SVG:矢量图 -->
<svg viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>

5. Web Storage

JavaScript
// localStorage:永久存储(同源 5-10 MB)
localStorage.setItem('user', JSON.stringify({ name: '张三' }))
localStorage.getItem('user')

// sessionStorage:会话级(关闭标签页即清空)
sessionStorage.setItem('token', 'xxx')

// 存储事件监听(同源其他页面修改 storage 触发)
window.addEventListener('storage', (e) => {
  console.log(e.key, e.oldValue, e.newValue)
})

6. Web Workers

JavaScript
// 主线程
const worker = new Worker('worker.js')
worker.postMessage({ cmd: 'compute', data: 1000000000 })
worker.onmessage = (e) => {
  console.log('计算结果:', e.data)
}

// worker.js(独立线程)
self.onmessage = (e) => {
  const data = e.data.data
  let sum = 0
  for (let i = 0; i < data; i++) sum += i
  self.postMessage(sum)
}

7. WebSocket

JavaScript
const ws = new WebSocket('wss://example.com/socket')
ws.onopen = () => ws.send('hello')
ws.onmessage = (e) => console.log('收到消息:', e.data)
ws.onclose = () => console.log('连接关闭')
ws.onerror = (err) => console.error('错误:', err)

8. Geolocation / Notification / Drag&Drop

JavaScript
// Geolocation API
navigator.geolocation.getCurrentPosition(
  pos => console.log(pos.coords.latitude, pos.coords.longitude),
  err => console.error(err)
)

// Notification API
Notification.requestPermission().then(permission => {
  if (permission === 'granted') {
    new Notification('标题', { body: '内容' })
  }
})

// Drag & Drop API
// 详见后文

三、H5 移动端适配

1. viewport

HTML
<!-- 常用且允许用户缩放的 viewport 配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
属性含义
width=device-width视口宽度等于设备宽度
initial-scale=1.0初始缩放比例
maximum-scale最大缩放
user-scalable是否允许用户缩放;普通页面不应禁用,以保障低视力用户可访问性
viewport-fit=cover适配刘海屏(iOS)

2. 移动端事件

JavaScript
// touchstart / touchmove / touchend
element.addEventListener('touchstart', (e) => {
  e.touches              // 当前屏幕上所有手指
  e.targetTouches        // 当前元素上的手指
  e.changedTouches       // 触发本次事件的手指
})

// 阻止默认行为(防止滚动、缩放)
element.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false })

// 300ms 点击延迟问题
// 早期移动浏览器可能等待双击缩放判断;现代页面使用规范 viewport 后通常没有该问题
// 不要把 click 机械替换为 touchend,也不再建议引入 FastClick;应保留键盘/辅助技术可用的语义化点击行为

3. rem / vw 适配

CSS
/* rem 方案(需要 JS 动态设置 html font-size) */
html { font-size: 14px; }
@media (min-width: 320px) { html { font-size: 14px; } }
@media (min-width: 375px) { html { font-size: 16.4px; } }
@media (min-width: 414px) { html { font-size: 18.4px; } }

.box { width: 10rem; height: 10rem; }

/* vw 方案(推荐,纯 CSS) */
html { font-size: calc(100vw / 7.5); }  /* 假设设计稿 750px */
.box { width: 1rem; height: 1rem; }

4. 1px 边框问题

CSS
/* 物理像素和 CSS 像素在 dpr=2 的设备上不一致
   CSS 的 1px 在屏幕上实际显示 2 物理像素
   解决:用 transform 缩放 */

.border-1px {
  position: relative;
}
.border-1px::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: #000;
  transform: scaleY(0.5);
  transform-origin: 0 0;
}

/* 或用 box-shadow */
.box { box-shadow: inset 0 -1px 0 0 #000; }

四、Web Components

Web Components 是一组浏览器原生支持的组件化方案,包含四个核心规范:

  1. Custom Elements(自定义元素)
  2. Shadow DOM(影子 DOM)
  3. <template><slot>
  4. ES Modules

1. Custom Elements(自定义元素)

JavaScript
// 1. 注册自定义元素
class MyButton extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })
    this.shadowRoot.innerHTML = `
      <style>
        button {
          background: #4CAF50;
          color: white;
          padding: 10px 20px;
          border: none;
          border-radius: 4px;
          cursor: pointer;
        }
      </style>
      <button><slot></slot></button>
    `
  }

  connectedCallback() {
    // 元素被添加到 DOM 时调用
    console.log('添加到 DOM')
  }

  disconnectedCallback() {
    // 元素从 DOM 移除时调用
    console.log('从 DOM 移除')
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 观察的属性变化时调用
    console.log(`${name}: ${oldValue} → ${newValue}`)
  }

  static get observedAttributes() {
    return ['color', 'size']
  }
}

customElements.define('my-button', MyButton)
HTML
<!-- 使用 -->
<my-button color="red">点击我</my-button>

2. Shadow DOM

JavaScript
// 创建 Shadow DOM
class MyCard extends HTMLElement {
  constructor() {
    super()
    const shadow = this.attachShadow({ mode: 'open' })
    shadow.innerHTML = `
      <style>
        .card {
          padding: 16px;
          background: #fff;
          border-radius: 8px;
          box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
      </style>
      <div class="card">
        <slot name="title"></slot>
        <slot></slot>
      </div>
    `
  }
}
customElements.define('my-card', MyCard)

Shadow DOM 的隔离特性

  • Shadow DOM 内部的样式不污染外部
  • 外部普通选择器不会穿透 shadow tree,但继承属性、CSS 自定义属性、::part():host 等机制仍可影响内部样式
  • mode: 'open' 时外部脚本可通过 shadowRoot 访问内部;closed 只让 element.shadowRoot 返回 null,不是强安全边界,持有内部引用或注入页面上下文的代码仍可能访问
HTML
<!-- 外部样式想影响 Shadow DOM:用 CSS variables -->
<style>
  my-card {
    --card-bg: #f5f5f5;
    --card-padding: 20px;
  }
</style>
CSS
/* Shadow DOM 内部使用变量 */
.card {
  background: var(--card-bg, white);
  padding: var(--card-padding, 16px);
}

3. Template 和 Slot

HTML
<template id="myTemplate">
  <style>
    .wrapper {
      padding: 16px;
    }
  </style>
  <div class="wrapper">
    <h2><slot name="title">默认标题</slot></h2>
    <div><slot>默认内容</slot></div>
  </div>
</template>
JavaScript
class MyPanel extends HTMLElement {
  constructor() {
    super()
    const template = document.getElementById('myTemplate')
    const templateContent = template.content

    this.attachShadow({ mode: 'open' })
    this.shadowRoot.appendChild(templateContent.cloneNode(true))
  }
}
customElements.define('my-panel', MyPanel)
HTML
<my-panel>
  <span slot="title">自定义标题</span>
  <p>这是自定义内容</p>
</my-panel>

4. Web Components 的生命周期

钩子调用时机
constructor()创建元素实例时
connectedCallback()元素被添加到 DOM
disconnectedCallback()元素从 DOM 移除
attributeChangedCallback()观察的属性变化
adoptedCallback()元素被 adoptNode 移动到新文档

5. Web Components 优缺点

优点

  • 原生支持:无需框架,原生组件化
  • 样式隔离:Shadow DOM 防止样式冲突
  • 框架无关:可与任何框架共存
  • 轻量:运行时开销小

缺点

  • 生态弱:相比 Vue/React 缺少成熟的工具链
  • 状态管理弱:没有内置响应式系统
  • SSR 难:服务端渲染需要 polyfill
  • 样式穿透繁琐:需要 CSS variables / ::part()
  • 学习成本:API 较新,文档相对少

6. 跨组件通信

JavaScript
// 方式1:CustomEvent
class ChildEl extends HTMLElement {
  fireEvent() {
    this.dispatchEvent(new CustomEvent('my-event', {
      detail: { msg: 'hello' },
      bubbles: true,        // 是否冒泡
      composed: true        // 是否可穿透 Shadow DOM 边界
    }))
  }
}

document.querySelector('child-el').addEventListener('my-event', (e) => {
  console.log('收到:', e.detail.msg)
})

// 方式2:属性 + 监听
childEl.setAttribute('data-value', 'hello')

// 方式3:共享 store
// 自定义一个事件总线,或使用 window 自定义事件

五、面试高频问答

Q1: 什么是 HTML 语义化?为什么要语义化?

:HTML 语义化是使用恰当的 HTML 标签来表达内容的结构和含义(如 <header> 表示头部、<article> 表示文章),而不是用无语义的 <div> 堆砌。优点:

  • 代码可读性和可维护性
  • SEO 友好(搜索引擎更准确地理解页面)
  • 可访问性(屏幕阅读器依赖语义化标签)
  • 团队协作的统一约定

Q2: <section><article> 怎么区分?

  • <article>:独立可分发的内容(博客文章、新闻、帖子)。脱离上下文仍能完整表达含义。
  • <section>:主题性分组(页面中的几个章节)。脱离上下文可能含义不完整。

简单记忆:能用 RSS 订阅的内容用 <article>,其他用 <section>

Q3: Shadow DOM 解决了什么问题?

:Shadow DOM 提供了样式和 DOM 的封装隔离。在大型应用或第三方组件库场景下:

  • 组件内部样式不污染外部(防止全局污染)
  • 外部样式也不会意外影响组件内部(防止被覆盖)
  • 避免类名冲突(不需要 CSS Modules / BEM)

Q4: Web Components 和 Vue 组件有什么区别?

维度Web ComponentsVue 组件
框架依赖原生无依赖需要 Vue
响应式需要手动实现内置响应式
生态较弱成熟
样式隔离Shadow DOMscoped / CSS Modules
学习成本新 APIVue 心智模型
SSR完善
最佳场景跨框架共享的微组件应用级组件库

现代方案通常是混合:跨框架的基础组件用 Web Components,应用层用 Vue/React。

Q5: viewport 中 width=device-width 的意义?

:在不设置 viewport 的情况下,移动浏览器会假设页面是桌面端(宽度 980px),导致页面被缩放显示。width=device-width 让 viewport 宽度等于设备宽度,CSS 像素与设备像素一一对应(在高 DPR 设备上仍受 dpr 影响),从而实现正常的移动端布局。

六、关联文档