星号选择器不匹配伪元素、注释节点、文本节点、document根节点、SVG非渲染节点及iframe内元素;在Shadow DOM中需:host 或::slotted(*);影响性能且难维护,应限定范围或显式列举元素。

星号选择器 * 真的会匹配“所有元素”吗?
不会。它只匹配「文档树中所有非伪元素的节点」,不包括 ::before、::after、::placeholder 这类伪元素,也不匹配注释节点、文本节点或文档根节点(document 本身)。实际开发中,它常被误认为能重置一切样式,但连 和 都要显式写进去才生效。
-
*不匹配 SVG 元素内部的<defs></defs>、<style></style>等非渲染节点 - 在 Shadow DOM 中默认不穿透,需配合
:host *或::slotted(*)才能作用于插槽内容 - 若页面含多个
<iframe></iframe>,*对子 iframe 内容完全无效
用 * 做全局 box-sizing 更安全的写法
很多人直接写 * { box-sizing: border-box; },看似省事,但会意外影响 SVG 的 <svg></svg>、<foreignobject></foreignobject> 甚至某些 UI 库的 canvas 容器——它们依赖 content-box 计算尺寸。更稳妥的做法是限定范围。
- 优先用
*, *::before, *::after { box-sizing: border-box; }—— 补上伪元素,避免伪元素仍按content-box渲染导致布局错位 - 对 SVG 场景,加例外:
svg *, svg *::before, svg *::after { box-sizing: content-box; } - 若项目用 Web Components,建议改用
:host * { box-sizing: border-box; },避免污染全局
* 的性能代价比你想象中大
浏览器解析 * 时,会为每个新挂载的元素(包括动态插入的)做一次全量匹配检查。在长列表、高频 DOM 操作或低端设备上,可能触发强制同步布局(Layout Thrashing)。这不是理论风险,真实监控中见过 * 导致滚动掉帧。
- Chrome DevTools 的 “Rendering” 面板开启“Paint Flashing”,滚动时大片闪烁基本就是
*在反复重绘 - Webpack 构建时若用 css-loader + postcss-preset-env,
*无法被安全地 scope 化或 tree-shake - 替代方案:用
html, body, div, p, span, section, article, header, footer, nav, aside, main显式列举常用容器/文本元素,性能提升明显且覆盖率达 95%+
为什么 * + * 和 * > * 很少真正有用
这两个组合看起来强大,实则极易失控。* + * 会命中所有相邻兄弟关系(比如 <p></p>
<p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p>
<div></div>),但现代框架生成的 DOM 常含大量空文本节点或注释,导致选择器行为不可预测;* > * 则几乎等价于 body > *,因为只有顶层元素才满足“父元素是任意元素”的条件——而这个“任意父元素”大概率就是 或 <shadow-root></shadow-root>。
-
* + *在 Vue/React 组件中会错误匹配组件边界处的换行符生成的文本节点,导致样式错乱 -
* > *实际效果接近body > *, html > *,但更难调试、更难覆盖 - 真需要相邻样式,直接用语义化类名,比如
.section + .section { margin-top: 2rem; },稳定且可维护
最麻烦的不是语法,是它看起来太简单,结果让人忽略层级、Shadow DOM、动态插入和伪元素这些真实存在的边界条件。写之前先问一句:这个“所有”,到底是哪个上下文里的所有?


















