Um DOM virtual leve ao estilo do Snabbdom. Ele representa sua interface com objetos JavaScript comuns (os VNode), compara uma árvore velha com uma nova e aplica ao DOM real só as diferenças.
init() monta o reconciliador e devolve uma função patch. Os módulos que vêm junto (class / props / attrs / style / events) se registram sozinhos.
patch(oldVnode, newVnode) monta uma árvore (quando oldVnode é um elemento real do DOM) ou compara duas árvores de vnodes e atualiza o DOM ali mesmo.
h(sel, dataOrChildren?, children?) é o auxiliar de hyperscript que constrói os VNode.
Nota: nesta implementação o init()não recebe argumentos: o conjunto de módulos é fixo e se registra por dentro, então não é preciso passar cada *Module para o init. Eles são exportados para consulta e inspeção.
import { init, h } from 'ranuts/vnode';// init() devolve uma função `patch`.// Os módulos que vêm junto (class, props, attrs, style, events) se registram sozinhos.const patch = init();const container = document.getElementById('app');// Monta uma árvore de vnodeslet vnode = h('div#app.container', { style: { color: 'red' } }, [ h('h1', 'Hello vnode'), h('button', { on: { click: () => console.log('clicked') } }, 'Click me'),]);// Primeiro desenho: passe um elemento real do DOM como vnode velho para montar dentro delepatch(container, vnode);// Depois: monte a árvore atualizada e aplique sobre ela o vnode anterior.// Ao DOM chegam só as diferenças (texto, estilo, ouvintes).const newVnode = h('div#app.container', { style: { color: 'green' } }, [ h('h1', 'Hello again'), h('button', { on: { click: () => console.log('clicked') } }, 'Updated'),]);patch(vnode, newVnode);vnode = newVnode; // guarde a última árvore para o próximo patch
// só a tagh('div');// tag + datah('div', { class: { active: true } });// tag + um único filho de textoh('span', 'hello');// tag + array de filhosh('ul', [h('li', 'one'), h('li', 'two')]);// tag + data + filhosh('a', { attrs: { href: '/home' } }, 'Home');// Seletores ao estilo do CSS definem o id e as classesh('div#main.card.large', 'content'); // <div id="main" class="card large">content</div>// O espaço de nomes do SVG entra sozinho quando o seletor começa com "svg"h('svg', { attrs: { width: 100, height: 100 } }, [h('circle', { attrs: { cx: 50, cy: 50, r: 40 } })]);
É o que o init() devolve. Na primeira chamada, passe um Element real do DOM como oldVnode para montar a árvore dentro dele. Nas seguintes, passe o VNode anterior para comparar e atualizar ali mesmo. Devolve o novo VNode, que você guarda como valor «velho» para a chamada seguinte.
Propriedades do DOM atribuídas com elm[key] = value
Record<string, any>
propsModule
attrs
Atributos HTML postos com setAttribute (true e false põem e tiram o atributo)
Record<string, string | number | boolean>
attributesModule
class
Classes condicionais: uma correspondência name → boolean
Record<string, boolean>
classModule
style
Estilos em linha: uma correspondência name → value (chaves --var viram variáveis CSS)
Record<string, any>
styleModule
on
Ouvintes de evento: event → handler (ou um array de manipuladores)
Record<string, Function | Function[]>
eventListenersModule
key
Identidade estável que o algoritmo de comparação usa para casar e reordenar os filhos
string | number
(o núcleo da comparação)
ns
URI do espaço de nomes (posta sozinha nas subárvores SVG)
string
(o núcleo da comparação)
hook
Ganchos de ciclo de vida por vnode (Hooks)
Hooks
(só como tipo: veja a nota)
Nota: o hook e o tipo Hooks fazem parte da superfície pública de tipos. Esta implementação enxuta move o DOM pelo ciclo de vida dos módulos (create / update / destroy); o laço atual do patch não chama os data.hook de cada vnode.
Só navegador. O ranuts/vnode mexe com document e com as APIs do DOM; importe-o em código de navegador, não no Node.
Guarde o último vnode. O patch devolve o novo VNode. Guarde-o e passe-o como oldVnode na atualização seguinte, para que as diferenças sejam calculadas contra a árvore vigente.
Num VNode, text e children se excluem: um nó é ou um nó de texto, ou um elemento com filhos.
Use key nas listas. Ao desenhar listas que mudam, dê aos irmãos valores de key estáveis, para que a comparação possa casar e reordenar os nós em vez de refazê-los.