<p>应优先使用 ID 或 data-* 属性选择器,避免多层嵌套;采用 BEM 命名“拍平”结构;限定查询范围并缓存高频结果;警惕伪类和属性前缀匹配的隐性开销。</p>

避免多层级嵌套的属性选择器拖慢 querySelector,关键不是“写得更完整”,而是让浏览器少做匹配验证。浏览器从右向左查找,越靠右的选择器越先被扫描;嵌套越深、属性条件越多,回溯路径就越长,查找步长自然拉长。
优先用 ID 或 data-* 单一标识符
ID 是浏览器原生哈希索引,查找时间复杂度为 O(1);data-id、data-role 等自定义属性也比类名更唯一、更可控,且现代浏览器对带 data- 前缀的属性有轻量级索引优化。
- ❌ 避免:
document.querySelector('div.container section.main article.content [data-title]') - ✅ 推荐:
document.querySelector('[data-id="article-123"]')或document.querySelector('#article-header') - 若 DOM 不可改,可在初始化阶段动态加
data-属性,一次设置,后续所有查询都受益
把嵌套结构“拍平”到类名里
用 BEM 类命名代替 DOM 层级依赖,让每个选择器只命中一个明确目标,不靠父容器“兜底”。这样既避开回溯,又提升语义清晰度和复用性。
- ❌ 避免:
document.querySelector('.card .card__header .card__title') - ✅ 推荐:
document.querySelector('.card__title')(前提是该 class 已直接加在目标元素上) - 同理,
.modal.is-open .close-btn→ 改为.modal-close-btn.is-visible
限定查询范围 + 缓存高频选择器
不要总在全局文档里大海捞针。缩小作用域能直接减少需遍历的节点数;对反复使用的路径,缓存结果比反复解析更省事。
- ✅ 限定上下文:
container.querySelector('[data-action="save"]')比document.querySelector('[data-action="save"]')快得多 - ✅ 缓存选择器:
const saveBtn = document.querySelector('[data-action="save"]');后续直接复用,避免重复解析和匹配 - ⚠️ 注意:不要在循环中反复调用
querySelector,尤其搭配宽泛选择器(如[data-field]),实测慢 3–6.8 倍
警惕“看起来简单”的隐性开销
有些选择器看似只有一两层,但内部触发全量扫描或字符串比对,实际性能更差。
- ❌
[class^="icon-"]:前缀匹配无法利用哈希索引,每次都要逐个比对字符串 - ❌
:not([disabled]) input:否定伪类迫使浏览器放弃快速路径,走全量扫描 - ❌
input[type="text"].form-control:两个条件叠加没提速,反而增加分支判断 - ✅ 替代思路:用
input.form-control-text这类组合类名,保持单层、可索引

















