Foto de redes sociales de Andre Taissin en Unsplash
La forma más sencilla de aumentar cualquier elemento integrado DOM:
No se necesitan polyfills , todos los navegadores modernos simplemente funcionan™️
Es posible ampliar HTML , SVG o cualquier otro espacio de nombres personalizado, como MathML , sin problemas.
Los elementos pueden crearse desde cero o actualizarse según sea necesario para una hidratación elegante.
cabe en 313 bytes (núcleo) o 774 bytes con devoluciones de llamadas del ciclo de vida de elementos personalizados incluidas (nada nuevo que aprender)
Este ejemplo se puede probar en vivo y apenas roza la superficie de lo que es posible hacer con este módulo extremadamente pequeño pero poderoso.
// const createRegistry = require('nonchalance');import createRegistry from 'nonchalance/core';// la exportación predeterminada acepta opcionalmente un contexto tipo `globalThis` // para todos aquellos entornos que no tienen un DOM nativo./ / Dicho registro puede exponer nombres de clases calificados más una referencia de `documento`// que se usará para crear elementos.const {HTML} = createRegistry();// extender cualquier elemento para crear o actualizar elementos personalizados elementos // de un registro que no comparte nada con la clase de contexto global La contraseña extiende HTML.Input { // heredará una etiqueta estática campo = "entrada" constructor(...args) {super(...args);this.type = 'contraseña'; } // evita que scripts maliciosos recuperen fácilmente cualquier contraseña obtener valor() {return '********'; } // caso de demostración: seguir usando el `valor` nativo cuando se establece la contraseña establecer valor (secreto) {super.value = secreto; }}document.body.innerHTML = ` <formulario> <tipo de entrada="texto" nombre="usuario" marcador de posición="usuario"> <tipo de entrada="contraseña" nombre="contraseña" marcador de posición="contraseña"> <entrada type="submit"> </form>`;// actualice el elemento deseado mediante new ClassTypenew Password(document.querySelector('[type="password"]'));// o cree una nueva instanciaconst secret = Object. asignar (nuevo Contraseña, { nombre: 'pase', valor: Math.random(), marcador de posición: 'contraseña de tiempo de ejecución'});const form = document.querySelector('form');form.insertBefore(secret, form.lastElementChild);
Usando el módulo de función personalizada, es posible actualizar cualquier tipo de elemento sin enfrentar el error de constructor ilegal que aparece cada vez que una class extends HTMLSomethingElement {}
causas de intención, cuando dicha clase no está definida globalmente como una entrada en el Registro customElements
.
No solo no hay nada compartido globalmente a través de este módulo en el contexto global, sino que todo trabajo extra incómodo para tener cualquier extensión incorporada funcionando es completamente innecesario:
Los elementos nuevos o pasados siempre conservan su cadena raíz prototipo.
nunca pueden ocurrir atributos adicionales o nombres contradictorios
Además de eso, debido a que se puede crear cualquier registro HTML para cada módulo o proyecto para compartir entre sus componentes, también es posible pasar a dicha creación de registro cualquier entorno globalThis
similar, falso o simulado, con al menos un campo document
que exponga un createElementNS(namespace, tagName)
y una o más clases que el proyecto debe probar, como HTMLElement
y/o cualquier otra necesaria para que dicho proyecto tenga éxito.
Sin embargo, dado que el objetivo principal de este módulo es el DOM , la referencia globalThis
se usa como valor predeterminado sensato, pero eso aún no significa que se comparta nada entre los registros creados a través de la exportación predeterminada.
No . La forma en que funciona custom-function
se puede resumir como sigue:
# a native <p> protoype chain HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to new (class CustomP extends HTML.P {}) CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node # a <p> passed to class AnotherP extends CustomP {} AnotherP -> CustomP -> HTMLParagraphElement -> HTMLElement -> Element -> Node
En pocas palabras, crear un elemento a través del new AnotherP
o actualizar un elemento a través del new AnotherP(liveParagraph)
simplemente actualiza la cadena del prototipo, sin requerir que el elemento abandone el DOM o cambie su naturaleza nativa, ya que eso se conserva en la cadena de herencia del prototipo. .
Resumen: los registros despreocupados simplemente actualizan elementos sin cambiar su naturaleza, exactamente de la misma manera que la integración nativa extiende el trabajo bajo el capó.
Sí , ya sea a través de /jsx
export o mediante /ref
.
Acerca de /jsx Consulte ¿Qué es la exportación /jsx? sección.
Acerca de /ref Consulte ¿ Qué es la exportación /ref? sección.
La exportación /ce
actualiza automáticamente los elementos de una manera compatible con los métodos de las clases connectedCallback
, disconnectedCallback
y attributeChangedCallback
, junto con su campo estático observedAttributes
.
El módulo utiliza una versión mejorada del módulo de elemento personalizado que ya funciona bien.
Vea esta demostración en vivo en codepen para tener una idea de cómo funciona.
No . Metafóricamente hablando, los elementos HTML tienen un significado semántico y una utilidad bien definida y deseada una vez que están activos, de la misma manera que una función JS será, para siempre, una función JS , incluso si Object.setPrototypeOf(() => {}, Number.prototype)
sucede... ¿puedes ver o estar de acuerdo en lo incorrecto que es eso?
Este módulo no quiere (y probablemente tampoco pueda) protegerse contra el mal uso de sus funciones, así que asegúrese de que cada vez que se actualice un elemento, conserve su cadena de prototipo nativa detrás de escena, o estará solo luchando contra el DOM . .. lo cual es bastante inconveniente, en mi opinión.
En resumen, de la misma manera que customElements.define('my-link', class extends HTMLDivElement {}, {extends: 'a'})
no tiene sentido, con suerte este módulo confía en que sus usuarios eviten las clases sin sentido.
Actualmente, la exportación principal/predeterminada para este módulo apunta a la misma exportación /core
.
Debido a que este módulo abre una caja de Pandora con su simplicidad y tamaño de código de vaporware, y principalmente porque todavía está detrás de una versión 0.
, estoy tratando de considerar qué debería incluirse en el índice, y aquí algunas de mis ideas:
¿No sería genial tener un módulo basado en ESX que entienda los componentes definidos de esta manera?
¿No sería genial tener una función pragma JSX que cree componentes a través de este módulo?
¿No sería genial tener... (tu lugar reservado aquí)...?
Sí, sería genial, y si puedo decidir cómo se debe nombrar la exportación predeterminada, estoy dispuesto a incluir ese nombre, entre otras bondades, como entrada predeterminada para este módulo... estad atentos o por favor dame pensamientos y sugerencias sobre cómo hacer eso
Hasta entonces, utilice exportaciones explícitas para asegurarse de que futuras actualizaciones no arruinen su lógica, y me disculpo si los cambios recientes le causaron problemas, pero estoy bastante seguro de que puede relacionarse o comprender fácilmente que fue para siempre.
Cuando los elementos se actualizan a distancia, es posible que estos tuvieran alguna propiedad adjunta que no tuvo la oportunidad de pasar a través de sus accesores.
Este asistente simplemente garantiza que las propiedades heredadas se eliminen como claves de elementos propios para luego activarse como descriptores de acceso inmediatamente después.
importar createRegistry desde 'nonchalance/ce'; importar accesores desde 'nonchalance/accessors';const {HTML} = createRegistry();class WithAccessors extiende HTML.Div { constructor(...args) {accesores(super(...args)); } obtener valor() {console.log('obtener valor', this._value);devolver this._value; } establecer valor(_valor) {this._value = _value;console.log('establecer valor', this._value); }}// elemento div nativoconst div = document.createElement('div');div.value = 123;// actualizadonuevo WithAccessors(div);// re-checkconsole.log(div.value);
Véalo en vivo para probar más.
La exportación /builtin
(248 bytes) es exactamente igual a /core
excepto que no utiliza custom-function
detrás de escena, lo que significa que:
no es posible crear new BuiltIn()
o un new BuiltIn(element)
ya que eso generaría un error, a menos que no esté ya registrado como extensión incorporada del elemento personalizado.
se puede utilizar para automatizar el registro de componentes, como se muestra en esta demostración en vivo en CodePen
La única advertencia importante en torno a esta exportación es que, debido a que se basa en elementos personalizados estándar reales, el polyfill incorporado podría ser necesario para Safari o WebKit, por ejemplo:
<!-- script de la página principal para Safari solo polyfill --><script>self.chrome ||self.netscape ||document.write('<script src="//unpkg.com/@webreflection/custom-elements -integrado"><x2fscript>');</script>
Tenga en cuenta que, si bien los espacios de nombres HTML
y SVG
están permitidos de forma predeterminada como extensiones integradas, los elementos personalizados no aceptan extensiones SVG , por lo que prácticamente solo las extensiones HTML son posibles con la exportación /builtin
actual.
La exportación ./dummy
está destinada principalmente a SSR y proporciona exactamente la misma utilidad para extender clases que solo llevarán un campo tag
estática.
Combinado con el /tag
es posible hacer 100% SSR con indiferencia e hidratarse a distancia.
importar createRegistry desde 'https://unpkg.com/nonchalance/dummy'; importar createTag desde 'https://unpkg.com/nonchalance/tag';const {HTML} = createRegistry();class HelloDiv extiende HTML.Div { connectCallback() {console.log('aquí estoy'); }}// crea un espacio de nombres reutilizable para hidratoconst nmsp = {HelloDiv};// crea una etiqueta transformadorconst etiqueta = createTag(nmsp);// imagina una respuesta del servidor en su lugar// nota: este código es solo para demostraciónconsole.log( etiqueta`<!doctype html><script type="module">importar createRegistry desde 'https://unpkg.com/nonchalance/ce';const {HTML} = createRegistry();const nmsp = {};for (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.dataset; eliminar el.dataset.comp; (el);}</script><HelloDiv></HelloDiv>` .unirse('') .recortar() .replace('const nmsp = {};',`const nmsp = { ${[...Object.entries(nmsp)].map( ([clave, valor]) => `${clave}: ${ valor}` ).join(',n')} };` ));
La exportación /jsx
(976 bytes) acepta una opción adicional createElement
y devuelve una función jsx
que se puede usar como pragma @jsx para transformar, entre todo lo que ya funciona de forma predeterminada en React o Preact , también clases extendidas a través del registro HTML o SVG . , incluidas todas las características que /ce
aporta a esas clases: Elementos personalizados como el ciclo de vida con algo de sabor encima:
las clases recibirán en su constructor los accesorios pasados a lo largo del elemento, habilitando señales, otras funciones o manejando cualquier cosa que ya sea posible para ser manejada por componentes JSX predeterminados.
cuando se llama al constructor, el elemento ya estaría lleno con sus hijos, evitando posibles travesuras conocidas con elementos personalizados estándar cuando las clases se definen/registran antes de que se analice el documento.
Sin embargo, similar a /builtin
extend, no es posible new Component(props)
pero siempre es posible <Component {...props} />
.
Véalo utilizado en la práctica con React en vivo en CodePen.
El DOM es el DOM , sin importar cuántas direcciones indirectas haya en el medio. Su DX puede variar, de acuerdo con las características del marco, pero si lo que busca es React , existe una forma pequeña pero elegante y basada en ref
de promover nodos JSX regulares con indiferencia/núcleo o indiferencia/ce :
import referenciado desde 'nonchalance/ref';// indica que el Componente se pasará como referencia// Nota: este es solo un Proxy ligero que otorga integridad de clase// independientemente de su uso en el componente wildconst = referenciado(la clase extiende HTML. División { constructor(...args) {super(...args);this.addEventListener('hacer clic', console.log); }});ReactDOM.render( <div ref={Component}>haz clic en mí</div>, documento.cuerpo);
La utilidad ref
también podría usarse como decorador y sin afectar ninguna característica de las clases regulares de indiferencia . Además, cada elemento se actualiza solo una vez para que sea seguro agregar oyentes o lógica en el constructor.
Vea esta demostración en vivo en codepen para probarla.
La exportación /selector
(796 bytes) permite la definición en vivo de cualquier selector CSS para que la clase relacionada actualice automáticamente cualquier elemento que coincida con ese selector.
Tenga en cuenta que dicho selector debe ser lo más exclusivo posible, de lo contrario podrían ocurrir sorpresas. Utilice clases específicas o atributos data-
para describir mejor su selector.
importar createRegistry desde 'nonchalance/core'; importar { definir } desde 'nonchalance/selector'; const { HTML } = createRegistry(); const Special = define('[data-comp="special"]', la clase extiende HTML. División { constructor(...args) {super(...args);this.textContent = '¡Soy especial!'; }});// contendrá "¡Soy especial!" texto una vez livedocument.body.innerHTML = `<div data-comp="special"></div>`;
La exportación ./tag
(188 bytes) permite la transformación de plantillas de una manera amigable con la hidratación.
Se puede usar como valor intermedio detrás de etiquetas literales de plantilla con capacidad total y la hidratación puede ocurrir una vez que esos elementos lleguen al DOM .
importar createRegistry desde 'indiferencia/ce'; importar createTag desde 'indiferencia/etiqueta';const {HTML} = createRegistry();clase HelloDiv extiende HTML.Div { connectCallback() {console.log('aquí estoy'); }}// crear un espacio de nombres reutilizable para hidratarconst nmsp = {HelloDiv};// crear una etiqueta transformadorconst tag = createTag(nmsp);// demodocument.body.innerHTML = tag`<HelloDiv />`.join( '');// ejemplo de hidratación para (const el of document.querySelectorAll('[data-comp]')) { const {comp} = el.conjunto de datos; eliminar el.dataset.comp; // actualiza el elemento una vez nuevo nmsp[comp](el);}
Véalo en vivo en CodePen.
Sí . Tanto las exportaciones /core
como /ce
permiten crear, de forma predeterminada, registros HTML y SVG :
importar createRegistry desde 'nonchalance/core';const {HTML, SVG} = createRegistry();class Circle extiende SVG.Circle { constructor(opciones) {Objeto .assign(super(), opciones) .setAttribute('relleno', 'oro'); } establecer cx(valor) { this.setAttribute('cx', valor) } establecer cy(valor) { this.setAttribute('cy', valor) } establecer r(valor) { this.setAttribute('r', valor) }}document.querySelector('svg').append( nuevo círculo({cx: 100, cy: 100, r: 50}));
Véalo en vivo en codepen.
También es posible pasar cualquier espacio de nombres a createRegistry(options)
, usando {MathML: "http://www.w3.org/1998/Math/MathML"}
como ejemplo.
Se permite cualquier espacio de nombres que tenga un significado para document.createElementNS
, no hay limitación en cuanto a qué tipo de elementos DOM podemos actualizar.
Tuve una respuesta muy larga a esto antes, pero el resumen es que este módulo utiliza estándares proporcionados por W3C , WHATWG o ECMAScript , y requiere menos de 1 KB para funcionar en todas partes.
Esto no es un polyfill, es una utilidad que lo ayudará a escribir componentes en el mundo JS y no tendrá que preocuparse de que entren en conflicto, requieran herramientas o no sean portátiles en cualquier proyecto de destino que le guste/necesite/prefiera.
En resumen, si está de acuerdo en agregar menos de 1K bytes para entregar componentes universales tanto para el mundo Front End como para el Back End, ¿ha elegido el módulo correcto?
No hay nada más liberador que ser un niño descuidado que juega en el barro contra todos los que piensan " ¡no hagas eso! ".
Este módulo de alguna manera representa esa sensación a través de la libertad que ofrecen las características modernas de JS, mostrando una alternativa elegante, portátil y súper liviana a la complejidad cada vez mayor que ofrecen los proveedores de navegadores y las especificaciones modernas, todo necesario para obligar a los desarrolladores a solucionar la capacidad de simplemente extender. incorporados y preservar tanto la simplicidad como la gran accesibilidad por la que la Web es famosa.