Scalar nace de la necesidad de crear sistemas escalables, de alto rendimiento y no obstructivos usando los últimos estándares de programación web, lo cual incluye el uso de las últimas características basadas en ECMAScript.
El desarrollo de aplicaciones con scalar se basa en componentes no obstructivos, lo cual quiere decir que su funcionamiento no depende enteramente de javascript; obviamente muchas de las decisiones de que tan obstructivo puede llegar a ser scalar depende en gran medida del desarrollador.
Scalar es agnóstico al backend. No importa si tu HTML es generado por NodeJS, PHP, Python, Go o es un archivo estático. Scalar trata al HTML como la fuente de verdad absoluta. Su motor de hidratación despierta el marcado existente mediante selectores CSS, inyectando comportamiento y reactividad sin destruir la estructura original, lo que lo hace el framework más rápido y ligero para estrategias de SEO y SSR.
Existen dos formas de usar scalar en un proyecto; la primera es mediante node y npm ejecutando el comando npm i scalar, la segunda es usar el CDN.
<h1 data-bind="msg" id="hello-world"></h1>
<script src="https://unpkg.com/scalar"></script>
<script defer>
new scalar.Module()
.compose('#hello-world', ($) => ({
mount: () => $.msg = 'Hello world!!!'
})).execute();
</script>En ambiente de desarrollo es imprescindible contar con node y git en sus últimas versiones.
Clona o copia desde el repositorio principal de scalar.
git clone git@github.com:mirdware/scalar.gitIngresa a la carpeta del proyecto y descarga las dependencias.
cd scalar && npm installUna vez instaladas es posible ejecutar un servidor webpack con el comando npm run serve, construir el proyecto con npm run build o ambas mediante npm start.
Un módulo es un objeto javascript que se instancia de la clase Module de scalar, se deben proveer por constructor las dependencias para luego declarar cada uno de los componentes mediante el método compose('#selector', customComponent), esto se logra enviando como primer parámetro el selector del elemento y como segundo la función o clase conductual.
El método add(url, loader) se encarga de cargar un módulo junto a todos sus componentes cuando el navegador esta en la url especificada.
Caution
Desde la versión 0.3.5 el método add(url, loader) ha sido declarado obsoleto y no debe usarse bajo ninguna condición ya que sera eliminado sin ningún reemplazo.
Mediante compose solo se declara el componente en el módulo pero no se ejecuta dentro de la estructura de la página; para esto se debe hacer uso el método execute().
import { Module } from 'scalar';
import formComponent from './components/form';
const module = new Module();
module.compose('#hello-world', formComponent)
.execute();La ejecución del componente genera un compound object(Objeto compuesto) que contiene las propiedades enlazadas a la plantilla mediante data-bind y/o data-attr, este enlace varia de uno a doble sentido según sea el caso.
<input type="text" value="scalar" data-bind="name" />
<sc-hi data-attr="name:name">
<p slot="body">Probando <a href="">link</a> desde fuera.</p>
</sc-hi>Note
Las expresiones JavaScript en data-attr también se evalúan mediante Function, por lo cual aplica la misma restricción CSP. Si solo se usan propiedades simples sin expresiones (data-attr="class:active") esta evaluación no ocurre.
El framework gestiona automáticamente la limpieza de componentes mediante un MutationObserver: cuando un nodo con un componente es removido del DOM, se liberan sus event listeners y se dispara el evento unmount.
Si necesitas reasignar comportamiento a un selector o reiniciar el módulo, usa module.dispose(). Este método libera los componentes behavioral del módulo, dispara unmount en cada uno, y resetea el contenedor de dependencias, permitiendo que execute() corra nuevamente con un estado limpio.
Warning
dispose() no afecta web components — su registro en customElements es permanente por especificación. Solo los behavioral components pueden ser liberados y recompuestos.
Los servicios en scalar desde la versión 0.3.4 son autowire, esto quiere decir que no se necesitan declarar dentro del módulo. Si dos servicios necesitan comunicarse entre sí, el patrón recomendado es un tercer servicio de tipo EventBus. El servicio es inyectado mediante el método inject del compound object o de la función enviada a cada servicio.
import Logger from './logger';
class Service {
constructor(inject) {
this._logger = inject(Logger);
}
}Es posible mockear o falsear las dependencias mediante el método bind(Message, Fake), de esta manera cada vez que se solicite la dependencia Message se entregara una instancia de Fake.
Important
Desde la versión 0.3.5 se recomienda el uso de decoradores o propiedades estaticas para la inyección de dependencias otros métodos estan deprecados.
import { inject } from 'scalar';
import Logger from './logger';
@inject(Logger)
class Service {
constructor(logger) {
this._logger = logger;
}
}De esta manera se logra desacoplar el framework de los servicios, en caso de no querer importar inject se puede hacer mediante propiedades estaticas.
import Logger from './logger';
class Service {
static _providers = [Logger];
constructor(logger) {
this._logger = logger;
}
}Para usarse en behavioral functions se usa como decorador de la función y se inyectan como argumentos despues del objecto compuesto.
import { inject } from 'scalar';
import Logger from './logger';
export default inject(Logger)(($, logger) => ({
mount: () => {
logger.info("¡Inyectado!");
$.msg = "Datos listos";
}
}));Existen diferentes maneras de generar un componente; la primera es extendiendo de la clase Component de scalar con lo que deberá implementar el método listen que retorna un behavioral object(Objeto conductual).
export default class ToDo extends Component {
listen() {
return {
submit: () => add(this),
'.close': { click: (e) => remove(this, e) },
'.check': { click: (e) => crossOutItem(e) },
'#clean': { click: () => this.tasks = [] }
};
}
}Para generar web components el funcionamiento es muy similar al de componentes basados en clases, con la diferencia que se debe agregar el decorator @customElement con los estilos y template para dicho componente, más adelante se detallara el uso de este método.
Otra manera es mediante behavioral function(función conductual) la cual es una función pura de javascript que retorna las acciones del componente; la función recibe como parámetro un objeto compuesto y retorna un objeto conductual.
export default ($) => ({
submit: (e) => {
if (!$.task) return;
$.tasks.push($.task);
$.task = "";
},
'.close': {
click: (e) => {
const index = e.target.parentNode.dataset.index;
$.tasks.splice(index, 1);
}
},
'#clean': { click: () => $.tasks = [] }
});Al ser una función javascript pura se pueden usar diferentes estilos de programación. En el ejemplo anterior vimos un retorno directo del objeto, pero también es posible usar clousures.
export default ($) => {
function remove(e) {
const index = e.target.parentNode.dataset.index;
$.tasks.splice(index, 1);
}
function add() {
if (!$.task) return;
$.tasks.push($.task);
$.task = "";
}
return {
submit: add,
'.close': { click: remove },
'#clean': { click: () => $.tasks = [] }
};
};Incluso es posible usar las últimas características de ECMAScript para encapsular llamadas a otras funciones en módulos.
function remove($, e) {
const index = e.target.parentNode.dataset.index;
$.tasks.splice(index, 1);
}
function add($) {
if (!$.task) return;
$.tasks.push($.task);
$.task = '';
}
export default ($) => ({
submit: (e) => add($),
'.close': { click: (e) => remove($, e) },
'#clean': { click: () => $.tasks = [] }
});Mediante javascript se establece el comportamiento de un componente por lo cual se debe retornar un objeto con selectores CSS o nombres de evento (click, submit, reset, blur, focus, etc) como llaves, en el primer caso su valor deberá ser otro objeto con similares características y en el segundo contendrá el manejador del evento, a esta estructura se le denomina objeto conductual.
Los eventos lanzados por un objeto conductual tienen un comportamiento de burbuja y son pasivos, si se antepone el signo _ a su nombre este prevendrá su acción por defecto y se volvera activo, si es necesario se puede retornar true para ejecutar el comportamiento del elemento pero esto no modificara el hecho que el evento a dejado de ser pasivo; si el signo aparece después del nombre quiere decir que sé está forzando la propagación en modo captura, es posible usar ambas características a la vez.
return {
_submit: (e) => add($),
'.close': { _click: (e) => remove($, e) },
'#clean': { click: () => $.tasks = [] }
}Tip
Se recomienda usar atributos data-action como selectores en el objeto conductual en lugar de clases o IDs, separando así el comportamiento del estilo: { '[data-action="save"]': { click: save } }.
El evento mount se ejecuta tan pronto como el componente es instanciado, hidratado y vinculado al DOM. Es el lugar ideal para establecer el estado inicial, inyectar servicios y realizar cualquier operación que dependa del marcado. Se debe priorizar el uso de mount sobre el constructor para ejecutar efectos secundarios (como llamadas a APIs o suscripciones), ya que esto garantiza que la lógica pueda ser reiniciada limpiamente por el sistema de Hot Module Replacement (HMR) o re-hidratada sin duplicar recursos.
El evento unmount es el complemento de mount. Se ejecuta justo antes de que el componente sea liberado, ya sea porque su nodo fue removido del DOM mediante manipulación dinámica o porque se invocó el método module.dispose(). Es obligatorio usar este evento para liberar recursos externos: limpiar timers, abortar peticiones de red o eliminar listeners agregados globalmente a window o document.
El evento mutate se dispara cada vez que Scalar transforma el DOM de un elemento debido a una mutación de su propiedad enlazada.
Es el punto de integración perfecto para librerías de terceros. Por ejemplo, si scalar inyecta dinámicamente un input, el desarrollador puede escuchar el evento mutate en ese elemento para inicializar sobre él un DatePicker o un InputMask, asegurando que el componente externo se mantenga sincronizado con el HTML reactivo.
Los eventos mount, unmount y mutate se despachan con bubbles: true y composed: true, por lo que atraviesan shadow boundaries y burbujean hacia el árbol del documento. Es posible observarlos desde un componente padre o desde una referencia externa a un elemento interno del shadow DOM.
return {
mount: () => message.my = $.my,
'.input': { mutate: (e) => mask(e.target) }
}Es posible hacer uso de servicios mediante el método inject(Message) enviando como parámetro la clase que fue proveída al módulo, si esta no fue declarada se retornara undefined.
Warning
Desde la versión 0.3.5 no se debe usar inject en su lugar se debe usar el decorador @inject o directamente la función inject para behavioral function.
Para hacer uso de un arreglo dentro de un componente se debe establecer un data-key que sirva como índice del elemento, posteriormente se obtiene mediante el método getIndex(e) el cual recibe el evento como parámetro.
Warning
Desde la versión 0.3.5 no se debe usar getIndex en su lugar se debe incluir el context parameter, en un futuro se cambiara el comportamiento de data-key para soportar keyed reconciliation.
Cuando manejas eventos en listas, el parameter context que recibe la función es un Proxy vinculado al estado original. Si modificas una propiedad del context directamente (ej: task.done = true), Scalar detectará el cambio y actualizará automáticamente la fila correspondiente en el DOM sin que tengas que manipular el array principal.
'.list label': { change: (_, item) => toogleItem(item, this) },
'.optext span': { _click: (e, badge) => removeBadge(e, badge, this) },Un componente puede generar otro mediante el método compose($domElement, customComponent), este último se denomina componente derivado, ya que su creación no se realizó desde un módulo sino que deriva de un similar. Se puede hacer uso de los métodos de un derivado ya que compose retorna el objeto compuesto.
const index = $.getIndex(e);
const $domElement = await $.inject(Modal).open('https://sespesoft.com/resource', 'Recurso');
const response = await $.compose($domElement, modalCOmponet).send({ index });Scalar genera automáticamente propiedades reactivas al parsear el DOM mediante los atributos data-bind o data-attr. Si una propiedad ya ha sido declarada en el componente antes del parseo, Scalar realizará una conciliación de estado, priorizando el valor presente en la vista (fuente de verdad del HTML).
Los métodos nativos de arrays y objetos (como Array.indexOf, Array.find, Array.includes, etc) funcionan de manera transparente aunque se utilicen Proxies como argumentos. El framework realiza un unwrapping automático (desenvuelve el Proxy al objeto real) antes de ejecutar la lógica nativa, garantizando la compatibilidad total con el estándar ECMAScript.
Las computed properties son propiedades especiales del objeto conductual de solo lectura que se procesan cada vez que una propiedad enlazada a la computed function es modificada, estas deben establecerse como funciones de primer orden al cargar el componente, de primer orden quiere decir que si se establace un objeto, las funciones de este objeto no seran tratadas como computed properties, si no como métodos del objeto.
listen() {
return {
mount: () => {
this._total = () => this.tasks.length;
this._pending = () => this.tasks.filter(task => !task.checked).length;
}
};
}Las propiedades usadas dentro de la función no deben ser modificadas, solo deben servir en modo lectura como base para recalcular las computed properties. Existen dos momentos en el ciclo de vida la computed function: tracking y execution, en tracking se detecta automáticamente qué propiedades se utilizan para crear el grafo de dependencias y en execution se evalua la computed property cada vez que sus dependencias mutan.
Tip
El uso de _ se usa como convención para propedades de solo lectura. Las computed properties son la alternativa recomendada para entornos con políticas CSP estrictas. Al ser funciones de JavaScript puro, no requieren la evaluación dinámica de strings (eval/Function), evitando así las restricciones de unsafe-eval.
Durante la evaluación de una computada, se accede a los objetos sin envoltorios Proxy. Esto evita el desbordamiento de la pila de llamadas (stack overflow) y permite que métodos como .filter o .map se ejecuten a velocidad nativa del motor de JavaScript.
El solapamiento (overloaping) se presenta cuando se define un componente sobre otro ya establecido.
new Module()
.compose('.pageable', pageable)
.compose('.check-table', checkTable)
execute();<div class="pageable">
<form action="https://sespesoft.com/resource">
<input type="search" name="name" data-bind="name"/>
<input type="submit" value="Buscar"/>
</form>
<table class="check-table" data-bind="data">
<script type="text/template">
<tr>
<td>${data.one}</td>
<td>${data.two}</td>
<td><input type="checkbox" /></td>
</tr>
</script>
</table>
</div>En este caso tanto el componente pageable como checkTable hacen uso de la propiedad data, a esto hace referencia el solapamiento a compartir propiedades gracias a su ubicación dentro del DOM; un cambio en una propiedad afectara a la propiedad del componente solapado. Se debe tener cuidado al momento de solapar componentes pues es posible tener resultados inesperados, en muchas ocaciones lo recomendable es aislar cada comportamiento.
Tip
El solapamiento es una herramienta poderosa pero debe usarse deliberadamente. Si el flujo de datos entre componentes se vuelve difícil de rastrear, considera aislar el comportamiento mediante web components o un servicio compartido como EventBus.
En la versión 0.3.0 de scalar se da soporte al standard de web components, lo cual generá una dependencia a javascript; esto va en contra de generar componentes no obstructivos, así que se puede agregar o no esta caracteristica e ir escalando según las necesidades del proyecto.
La implementación de custom elements se realiza mediante el decorator @customElement el cual recibe las propiedades styles, template y type, este último es para soportar el estandard con el uso de diferentes elementos HTML.
@customElement({
template: '<strong>Hola <span data-bind="name">mundo</span>!!</strong>' +
'<slot name="body"><p>Por favor de click <a href="">aquí</a></p></slot>',
styles: 'strong{color:#f58731;} ' +
'img{vertical-align: middle; margin-right: 1em;} ' +
':host{border: 1px solid; display: block; border-radius: 1em; padding: .5em; margin: .5em;}'
})
export default class Greeting extends Component {}Como se puede observar el web component debe extender de Component y no de HTMLElement como lo hace el estandard, esto es para que la libreria pueda manejar cosas como el shadown DOM y ciertas funciones del ciclo de vida que se integran en el comportamiento normal de un backend component.
Es importante mencionar que el componente puede hacer uso de todos los métodos del ciclo de vida del custom element como: attributeChangedCallback(name, oldValue, newValue), connectedCallback() o disconnectedCallback(); al basarse en el estandar es posible hacer uso de slots y templates.
Al igual que en los behavioral components, mount y unmount dentro del objeto retornado por listen() son los hooks del ciclo de vida gestionados por Scalar. connectedCallback y disconnectedCallback siguen disponibles para interactuar con el estándar Web Components directamente, pero para la lógica de inicialización y limpieza de recursos se recomienda mount/unmount ya que se ejecutan una unica vez.
El uso de observedAttributes se automatiza mediante el descubrimiento de propiedades en el constructor de la clase. Scalar identifica las propiedades inicializadas (que no comiencen con _ o $) y las registra como atributos observados.
export default class MultiSelect extends Component {
constructor() {
super();
this._currentFocus = -1;
this.placeholder = '';
this.required = false;
this.value = [];
}
}El constructor debe llamar al padre y proceder a declarar las propiedades, cabe resaltar el uso del caracter _ para iniciar ciertas declaraciones, estas son propiedades que no se exponen al custom element, al igual que aquellas que inicien con $, las propiedades deben inicializarse para indicar como debe observedAttributes tratar el nuevo valor, así cuando es booleano y se aplica al elemento este lo convertira a true o si es un objeto [] o {} se tratara de hacer un JSON.parse.
El framework convierte internamente el nombre del atributo de kebab-case a camelCase para la asignación de la propiedad. El método attributeChangedCallback del usuario, si se implementa, recibe el nombre original en kebab-case conforme al estándar Web Components. Las propiedades que comienzan con _ o $ no se convierten en observedAttributes ni deberían participar en binding bidireccional.
<multi-select placeholder="Seleccionar tipo de items" _current-focus="0" required>
<option value="0" selected>Card</option>
<option value="1" selected>Table</option>
<option value="2" selected>Column</option>
</multi-select>La propiedad _currentFocus no sera enlazada al custom element. El paso de attributos dinamicos se sigue usando mediante data-attr como se haria con cualquier componente, cabe resaltar que es posible enviar datos complejos como objetos o arrays, los cuales seran codificados en JSON para enviar, por lo cual estos pasaran como valor y cualquier modificación dentro del custom element no se vera reflejada en el componente padre que envio el objeto (inmutabilidad).
<auto-complete required="required" placeholder="Countries" data-attr="data:countries"></auto-complete>@inject(Message)
export default class MultiSelect extends Component {
constructor(message) {
super();
this.data = [];
this._message = message;
}
}Las únicas vias de comunicación entre custom element, componente padre son los atributos y CustomEvent y acceso público a los métodos para control imperativo. El enlace bidireccional directo no es posible en este tipo de comunicación y debe hacerse de forma explícita mediante las vías anteriores.
{
'multi-select': { changed: (e) => $.multi = e.detail }
}Es posible hacer uso de ambos tipos de componentes dentro de una misma aplicación, supongamos un .extenal-component compuesto.
<section class="external-component">
<input type="text" value="scalar" data-bind="name" />
<sc-hi data-attr="name:name" />
</section>Las plantillas (Templates) representan la parte más básica del sistema y se pueden clasificar en: prerenderizadas y JIT (Just In Time).
Las plantillas prerenderizadas son aquellas suministradas por el servidor y hacen parte integral del cuerpo de la respuesta, de esta manera se puede garantizar el funcionamiento de la aplicación si el cliente no activa JavaScript; la idea de la librería es ir "escalando" un proyecto según la limitación del cliente (accesibilidad).
Una plantilla scalar podría contener atributos data-bind y/o data-attr, los primeros generan un enlace en dos direcciones entre el objeto compuesto y la plantilla, siempre y cuando el elemento al cual se enlaza pueda introducir información; en caso contrario dicho enlace se establecerá en una sola dirección; el segundo modifica los atributos del elemento según se modifique alguna propiedad y por su naturaleza es unidireccional.
Mediante data-bind se crea un enlace a una propiedad del componente, por lo tanto debe tener el formato de una propiedad javascript, mientras data-attr puede tener tantos atributos separados por ; como se desee, un atributo es un par clave valor en donde la clave es el nombre del atributo y el valor la propiedad del componente o una expresión javascript que manejará los cambios de estado, en caso de ser una propiedad no definida en un data-bind esta se creara en el componente, si la propiedad se encuentra dentro de una expresión esto no será posible.
Los nombres reservados del sistema así como las variables globales (incluidas undefined, Infiniy y NaN) no pueden ser usadas como propiedades de un componente, porque el framework los protege para evitar colisiones con el ámbito global.
Warning
Aunque en la versión actual es posible usar cualquier propiedad desde la vista y automaticamente se crea en el componente, este comportamiento puede cambiar en futuras versiones en favor de evitar el solapamiento (overlaping) de propiedades.
Cuando se desea declarar un objeto desde el sistema de plantillas se debe separar con . cada una de las propiedades del mismo, esto aplica también para modificaciones de atributos como estilos.
<div id="square" data-attr="style: squareStyle">
<span data-attr="classList: action" class="open"></span>
<h2 data-bind="my.msg" data-attr="style.color:my.color" style="color:#fff">Mensaje inicial</h2>
</div>El principal uso de las plantillas JIT se encuentra restringido al enlace de datos cuando la propiedad de un componente es compleja (principalmente arreglos) y su función es generar código HTML de manera dinámica. Una propiedad es definida como compleja cuando dentro se haya un script tipo text/template.
<ul data-bind="tasks">
<script type="text/template">
<li data-key="${index}">
<span class="${data.checked}">${data.content}</span>
<a href="#" class="check">✔</a>
<a href="#" class="close">[x]</a>
</li>
</script>
</ul>Siempre que se quiera manipular un arreglo desde el componente este debe estar indexado por data-key de esta manera es posible hacer uso del método getIndex (en versiones futuras se usara para keyed reconciliation).
Tambien existe la posibilidad de realizar un enlace directamente al template tomando de esta manera al elemento padre como base; esto es útil cuando un elemento debe tener un enlace con otra propiedad, pero su contenido se debe manejar dinamicamente (ej. selects dependientes).
<select data-bind="select">
<option>One</option>
<option>Two</option>
<option>Three</option>
<script type="text/template" data-bind="dependencies">
<option>${data.name}</option>
</script>
</select>Se puede interpolar código javaScript mediante el uso de la notación template string ${}; dentro de la plantilla es posible acceder a dos propiedades index y data, la primera indica el índice del array y la segunda la información contenida en el mismo.
Tip
Las plantillas JIT utilizan Function internamente para evaluar interpolaciones ${}. Esto requiere que la política CSP del proyecto permita unsafe-eval. Si tu entorno tiene restricciones estrictas de CSP, las plantillas prerenderizadas no tienen esta limitación.
Antes de la versión 0.3.0 la forma común de enlazar datos a las propiedades de un array era mediante emparejamiento (pairing), esto se da cuando el template y el contenido del elemento son exactamente iguales y difieren solo en la iterpolación; de esta manera los datos interpolados que partan del objeto data y no sean expresiones se combierten en parte del array.
Se debe tener en cuenta que al usar paring mode no es posible realizar interpolaciones seguidas en una plantilla <p>${var1} ${var2}</p> ya que el remplazo podrian ser palabras con espacios o una de las interpolaciones estar vacia lo cual generaría efectos inesperados; en este punto lo recomendable seria usar una sola interpolación <p>${var1 + ' ' + var2}</p> o separarlas <p>${var1}<span>${var2}</span></p>.
Warning
El uso de data-attr en la fase de emparejamiento puede generar comportamientos inesperados por lo cual se desanconseja su uso y se recomienda la interpolación de attributos.
En recientes versiones se puede hacer uso de una nueva tecnica de enlace para datos complejos array.${index}.name y en la actualidad es el método por defecto a utilizar para poblar dinamicamente el array.
La notación de punto que incluye indices númericos generá error al usarse directamente desde javascript pero no desde el data-bind. Con esto el enlace se realiza directamente por lo cual no es necesario que el template y el contenido del elemento coincidan en lo absoluto (unpairing).
Aunque ya no sea necesario realizar emparejamiento, la hidratación de la pantalla continua realizandose desde el template por lo cual este debe ser bastante parecido al cargue inicial.
Para poder usar el modo de emparejamiento es necesario colocarlo explicitamente en el script de la plantilla mediante el atributo data-pairing.
<script type="text/template" data-pairing>
<li data-key="${index}">
<span class="${data.checked}">${data.content}</span>
<a class="check" href="#">✔</a>
<a class="close" href="#">[x]</a>
</li>
</script>Hidden DOM
Actualmente la idea de usar virtual DOM como mecanismo de actualización para las plantillas JIT se encuentra pospuesto, en su lugar se esta experimentando con el uso de un hidden DOM. El cual funciona como un documentFragment que no es adicionado al DOM en ningún momento, si no que sirve como referencia para saber exactamente cuales son los cambios que se deben realizar.
Si estas trabajando directamente sobre la carpeta src puedes activar el debug mode con la variable de entorno development.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})esto te permite entre otras cosas:
- Ejecutar el debuging visual mediante
alt+sy el cierre del mismo mediantealt+x. un componente con borde azul quiere decir que es un web component, si el borde es naranja es un behavioral comoponent y si es rojo es por peligro de solapamiento. A parte de componentes tambien es posible ver los 🔗 data-bind y ⚡ data-action. - Hacer consultas sobre componentes mediante la función
queryComponent(target)en donde el target debe ser un nodo del HTML como$0. - Tambien es posible ejecutar el modo debug visual con las funciones
debug.enable()ydebug.disable().
Scalar expone integración con HMR mediante el evento personalizado scalar-hmr-update. Al recibirlo, el framework recompone automáticamente los componentes afectados sin recargar la página, preservando el estado del DOM.
El evento espera en su detail los objetos _old y _new, que representan el módulo, clase o función conductual anterior y su reemplazo.
window.dispatchEvent(new CustomEvent('scalar-hmr-update', {
detail: { _old: OldComponent, _new: NewComponent }
}));Esta integración está disponible únicamente cuando process.env.NODE_ENV !== 'production'.
- 🔑 modificar el reordenamiento de elementos HTML por
keyed conciliation. - 🛡️ Implementar Compiled Templates para pre-procesar las plantillas JIT, eliminando el uso de
Functionen tiempo de ejecución y logrando compatibilidad total con cualquier política CSP.