Volver al inicio

Problemas de web components: rendimiento y API

El artículo analiza los problemas clave de web components: boilerplate excesivo para propiedades, bajo rendimiento debido al proxy del host, registro global con conflictos e inconvenientes de Shadow DOM. Se proporcionan benchmarks y ejemplos de código.

Por qué web components son lentos: benchmarks y código
Advertisement 728x90

Rendimiento de componentes web y desafíos con la API

Crear un componente web con solo una propiedad requiere una cantidad significativa de código usando la API de Elementos Personalizados. La implementación base solo permite atributos de tipo cadena, lo que obliga a serializar otros tipos de datos.

Ejemplo de un componente mínimo:

class MyFoo extends HTMLElement {

	_bar = 0
	get bar() {
		return this._bar
	}
	set bar( next ) {
		this.setAttribute( 'bar', next )
	}
	
	static observedAttributes = [ 'bar' ]
	attributeChangedCallback( name, prev, next ) {
		this[ '_' + name ] = next
	}
	
}

globalThis.customElements.define( 'my-foo', MyFoo )

Para soportar números, se añade serialización JSON con una bandera _muted_ para evitar recursión:

Google AdInline article slot
class MyFoo extends HTMLElement {

	_bar = 0
	get bar() {
		return this._bar
	}
	set bar( next ) {
		this._bar = next
		this._muted_ = true; try {
			this.setAttribute( 'bar', JSON.stringify( next ) )
		} finally { this._muted_ = false }
	}
	
	_muted_ = false
	static observedAttributes = [ 'bar' ]
	attributeChangedCallback( name, prev, next ) {
		if( this._muted_ ) return
		this[ '_' + name ] = JSON.parse( next )
	}
	
}

Esto introduce problemas con la escapación de cadenas de atributos y exige capas adicionales de abstracción para fechas, objetos y otros tipos.

Rendimiento: Microsegundos que arruinan los fotogramas

Los componentes web son órdenes de magnitud más lentos que los objetos JS normales debido a la proxyación en tiempo de ejecución. Comparación del tiempo de creación:

  • Objeto JS normal: ~0,01 μs
  • Componente web: ~1–3 μs

Mil componentes tardan entre 1 y 3 ms en crearse—una porción sustancial del presupuesto de 16 ms por fotograma. Los cambios de propiedades también consumen microsegundos:

Google AdInline article slot
  • Asignación simple en JS: nanosegundos
  • Setter en componente web: microsegundos

La causa raíz es el intercambio constante de datos entre JS y el runtime en C++ del navegador, bloqueando la optimización JIT. Uso de memoria: 124 bytes por componente web trivial frente a 16 bytes para un objeto JS.

Registro global y conflictos de nombres

El registro de componentes es global e irreversible hasta que se cierre la pestaña:

customElements.define( 'ya-button', YandexButton )
// ...
customElements.define( 'ya-button', YahooButton ) // 💥 Error: nombre ya usado

Los conflictos de nombres rompen la modularidad. Aunque el CustomElementRegistry en Shadow DOM ayuda parcialmente, no hay polyfills disponibles, y fusionar registros requiere gestión de espacios de nombres.

Google AdInline article slot

Alternativas:

  • Un registro fractal global como DNS
  • VerLess — actualizaciones sin romper compatibilidad

Ganchos de ciclo de vida y Shadow DOM

connectedCallback/disconnectedCallback se disparan ante cualquier movimiento del componente—incluso cuando se mueve al mismo lugar—activando cascadas en subárboles. Esto genera tareas asíncronas innecesarias.

Shadow DOM ofrece aislamiento pero complica el estilo: adoptedStyleSheets requieren importaciones dinámicas de estilos desde fuera. Los nombres de elementos deben ser estrictamente kebab-case, chocando con elementos estándar del DOM.

Lo que realmente importa

  • Rendimiento: Los componentes web son 100–1000x más lentos que los objetos JS por la proxyación del host
  • Código repetitivo: Serialización/deserialización es obligatoria para propiedades no de tipo cadena
  • Registro: Global, irreversible, propenso a colisiones de nombres
  • Ciclo de vida: Ganchos se activan en movimientos, causando cascadas
  • Shadow DOM: Dificulta la personalización de estilos y el uso de polyfills

Los componentes web no admiten tipado estático de TypeScript ni reactividad integrada, requiriendo frameworks para aplicaciones reales.

— Editorial Team

Advertisement 728x90

Leer después