语义标签是模块边界的硬约束,需严格遵循职责划分; 是需手动实例化的冷存储片段;bem 类名是样式作用域的物理锚点;构建时处理html片段才是真模块化;web components在ssr中需defer加载防白屏。

语义标签不是装饰,是模块边界的硬约束
用 <header></header>、<nav></nav>、<main></main>、<section></section>、<aside></aside>、<footer></footer> 划分结构,本身不等于模块化——只有当每个标签承载明确职责时,才构成可维护的模块边界。
常见错误现象:<main></main> 里嵌套 <aside></aside>,或把登录表单塞进 <nav></nav>;结果是无障碍工具识别错乱、SEO 权重分散、CSS 选择器越写越长。
-
<main></main>必须全页唯一,且不能作为<article></article>或<section></section>的父容器 -
<aside></aside>内容必须与邻近的<article></article>或<section></section>相关;全站广告位应放<footer></footer>或独立<section></section> - 连续三层
<section></section>嵌套(如<section><section><section></section></section></section>)会削弱语义,此时更适合用data-module+ JS 控制显隐
template 标签不是组件,是需手动实例化的占位符
<template></template> 不渲染、不执行脚本、不加载图片,直接 innerHTML 赋值会丢掉 <input checked> 状态,cloneNode 无法触发内联 <script></script> —— 它只是 DOM 片段的“冷存储”,不是开箱即用的组件。
安全克隆的关键是 document.importNode():
- 获取模板:
const tmpl = document.getElementById('card-template') - 深克隆内容:
const fragment = document.importNode(tmpl.content, true) - 填充数据:遍历
fragment.querySelectorAll('[data-bind]'),用textContent或innerHTML替换(注意 XSS) - 挂载:
container.appendChild(fragment),别用innerHTML = ...覆盖已有结构
禁止在模板里写 id="card-1",多次使用会导致 ID 冲突;也别用 {{title}} 插值语法——纯 HTML + JS 不识别,必须手写替换逻辑。
BEM 类名不是命名习惯,是样式作用域的物理锚点
写 class="card__title" 不是为了好看,而是让 CSS 规则绑定到具体模块上。没有构建工具时,BEM 是防止样式泄漏的最低成本方案。
容易踩的坑:
- 块名不能泛化:
class="main"或class="content"无法表达模块身份,应为class="product-card" - 元素必须是块的直接子节点:
product-card__image__caption违反 BEM,应改为product-card__caption - 修饰符只用于状态开关:
button--disabled合理,button--top不合理——位置属于布局职责,应由父容器 grid-area 或 flex 控制
若项目无 JS 构建流程,就别幻想 CSS Modules;改用 PostCSS 自动加前缀(如 .u-product-card__title),并在 HTML 中严格对应。
构建阶段注入才是真复用,include 是伪模块化
PHP <!--#include file="header.html"--> 或原生 SSI 看似简单,但开发时无法预览组件状态、热更新失效、样式作用域失控——它只是文件拼接,不是模块化。
真正可行的路径是构建时处理:
- 用
html-loader(Webpack)或vite-plugin-html加载.html片段,解析data-props属性传参 - 组件文件统一放在
components/下,命名带版本号(如navbar.v2.html),避免多人修改冲突 - 禁止在组件 HTML 内硬编码 class 名;所有样式通过 BEM + 构建时自动注入 scoped 前缀(如
navbar__logo → navbar-v2__logo)
Web Components 的 customElements.define() 很诱人,但 SSR 场景下服务端渲染出的 <my-card></my-card> 在客户端 hydration 前是空白的,必须配合 defer 加载脚本,否则首屏白屏——这点常被忽略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











