Element#matches() compiles the selector on every call
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 35/100
- Tipo de issue
- Refactorización
- Claridad
- Bien especificado
- Estado de actividad
- Estancado
- Stack tecnológico
- css, javascript
- Área
- backend, performance
Línea de trabajo
The hot path is esm/shared/matches.js near line 109, where Element#matches() passes the selector string to CSSselect.is(), which recompiles it on every call; prepareMatch already compiles once per call for querySelector* and closest(), so look there for the pattern to reuse. Pull request #349 is already open against this, so check it before starting. Done means a compiled selector is cached per selector string and document kind in a small bounded Map, :scope selectors still compile per call, and the existing matches tests pass with coverage unchanged.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Element#matches(selectors) parses and compiles the selector again on every call. Code that tests many elements against the same few selectors (resolving a <style> sheet over an SVG chart, for example) spends most of its time in css-what's parser and css-select's compiler rather than in matching.
// npm i linkedom && node matches.mjs
import { parseHTML } from 'linkedom';
let svg = '<svg id="chart">';
for (let i = 0; i < 400; i++)
svg += `<g class="node n${i % 7}"><rect class="box"></rect><g class="label"><text class="title"><tspan>${i}</tspan></text></g></g>`;
const { document } = parseHTML(`<!doctype html><html><body>${svg}</svg></body></html>`);
const selectors = [];
for (const a of ['#chart', '#chart .node', '#chart g.label', 'svg'])
for (const b of ['rect', 'text', 'tspan', '.label', '.title', '.box', '.n3', 'g', '.cluster rect', '.edgeLabel', 'g.node', '.missing'])
selectors.push(`${a} ${b}`);
const elements = [...document.querySelectorAll('svg, svg *')];
const t = performance.now();
for (const el of elements)
for (const sel of selectors)
el.matches(sel);
console.log(`${elements.length * selectors.length} calls: ${(performance.now() - t).toFixed(0)} ms`);
Node 22.17.0, macOS, median of runs:
96,048 matches() calls |
|
|---|---|
| linkedom 0.18.13 | ~112 ms |
linkedom main (3fcf145) |
~108 ms |
| same, with the compiled selector kept per selector string | ~20 ms |
A larger run (2,001 elements × 50 selectors, 300k calls), in ms per 100k calls: linkedom 0.18.13 158, main 111, with the compiled selector kept 17; happy-dom 21.0.3 10, jsdom 30.1.3 209.
A CPU profile of main shows css-what parseSelector/unescapeCSS, css-select compileToken/escapeRegex and GC on top; the matching itself is a small share.
Where: esm/shared/matches.js#L109 passes the selector string to CSSselect.is(), which calls compile(query, options) each time (css-select 7.0.0, dist/index.js, is). querySelector* and closest() compile once per call through prepareMatch, so it is matches() that pays per element.
Possible fix: keep the compiled function per selector string and document kind (xmlMode), in a small bounded Map; selectors with :scope keep compiling per call since their context is the element. I have this with a test (coverage unchanged) and can open a PR.
- Lenguaje dominante
- HTML
- Estrellas
- 2.1k
- Forks
- 114
- Merge medio
- 8 d 23 h
- PR fusionados (30 d)
- 2
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de WebReflection/linkedom
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
WebReflection/linkedom#352 · 7 comentarios ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
WebReflection/linkedom#344 ·
-
`TextElement.toString()` puts the element text back at the first `><` of the start tag, so an attribute value containing `><` swallows the text of `<style>`, `<script>`, `<title>` and `<textarea>`Posiblemente ocupada @vsolano9 la tomó hace 10 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
WebReflection/linkedom#341 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
WebReflection/linkedom#340 ·
-
Dificultad 3/5 Medio día Aptitud para principiantes 50/100
WebReflection/linkedom#345 ·
Todos los issues de WebReflection/linkedom
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
FootprintAI/Containarium#3182 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
VictoriaMetrics/victorialogs-datasource#747 ·
Los mantenedores suelen responder en 1 día
-
good first issue hardware help wanted no-box serve
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día