Virtuální DOM uzly
Co je virtuální DOM
Virtuální DOM strom je datová struktura v JavaScriptu, která popisuje DOM strom. Skládá se z vnořených virtuálních DOM uzlů, označovaných také jako vnode.
Při prvním vykreslení virtuálního DOM stromu se tento strom použije jako šablona pro vytvoření DOM stromu, který odpovídá jeho struktuře.
Virtuální DOM stromy se obvykle znovu vytvářejí při každém cyklu vykreslování, typicky v reakci na obslužné rutiny událostí nebo změny dat. Mithril.js porovnává strom vnode s jeho předchozí verzí a upravuje pouze ty DOM elementy, u kterých došlo ke změnám.
Může se zdát zbytečné tak často znovu vytvářet vnode, ale moderní JavaScriptové motory dokážou vytvořit stovky tisíc objektů za méně než milisekundu. Na druhou stranu, úprava DOM je o několik řádů nákladnější než vytváření vnode.
Z tohoto důvodu Mithril.js používá sofistikovaný a vysoce optimalizovaný algoritmus pro porovnávání virtuálního DOM, který minimalizuje počet aktualizací DOM. Mithril.js také generuje pečlivě vytvořené datové struktury vnode, které jsou kompilovány JavaScriptovými enginy pro téměř nativní výkon přístupu k datovým strukturám. Kromě toho Mithril.js agresivně optimalizuje i funkci, která vytváří vnode.
Mithril.js se snaží podporovat model vykreslování, který znovu vytváří celý virtuální DOM strom při každém vykreslení, aby poskytl deklarativní immediate mode API. Tento styl vykreslování výrazně usnadňuje správu složitosti uživatelského rozhraní.
Pro pochopení důležitosti immediate mode zvažte DOM API a HTML. DOM API je imperativní retained mode API a vyžaduje: 1. zápis přesných instrukcí pro sestavení DOM stromu procedurálně a 2. zápis dalších instrukcí pro aktualizaci tohoto stromu. Imperativní povaha DOM API znamená, že máte mnoho příležitostí k mikrooptimalizaci kódu, ale také zvyšuje riziko zavedení chyb a zhoršuje srozumitelnost kódu.
Naproti tomu HTML je bližší immediate mode vykreslovacímu systému. S HTML můžete psát DOM strom mnohem přirozenějším a čitelnějším způsobem, aniž byste se museli starat o to, že zapomenete připojit potomka k rodiči, narazíte na přetečení zásobníku při vykreslování extrémně hlubokých stromů atd.
Virtuální DOM posouvá možnosti HTML dál tím, že umožňuje psát dynamické DOM stromy bez nutnosti ručního psaní sad volání DOM API pro efektivní synchronizaci uživatelského rozhraní s libovolnými změnami dat.
Základy
Virtuální DOM uzly, neboli vnode, jsou JavaScriptové objekty, které reprezentují DOM elementy (nebo jejich části). Virtuální DOM engine Mithril.js používá strom vnode k vytvoření DOM stromu.
Vnode se vytvářejí pomocí pomocné funkce m()
hyperscript:
m('div', { id: 'test' }, 'hello');
Hyperscript může také používat komponenty:
// definice komponenty
var ExampleComponent = {
view: function (vnode) {
return m('div', vnode.attrs, ['Hello ', vnode.children]);
},
};
// použití komponenty
m(ExampleComponent, { style: 'color:red;' }, 'world');
// ekvivalentní HTML:
// <div style="color:red;">Hello world</div>
Struktura
Virtuální DOM uzly, neboli vnode, jsou JavaScriptové objekty, které reprezentují element (nebo jeho části) a mají následující vlastnosti:
Vlastnost | Typ | Popis |
---|---|---|
tag | String|Object | nodeName DOM elementu. Může to být také řetězec [ pokud je vnode fragment, # pokud je to textový vnode, nebo < pokud je to trusted HTML vnode. Kromě toho to může být komponenta. |
key | String? | Hodnota použitá k mapování DOM elementu na jeho příslušnou položku v poli dat. |
attrs | Object? | Mapa DOM atributů, událostí, vlastností a metod životního cyklu. |
children | (Array|String|Number|Boolean)? | U většiny typů vnode je vlastnost children pole vnode. Pro textové a trusted HTML vnode je vlastnost children buď řetězec, číslo nebo boolean. |
text | (String|Number|Boolean)? | Používá se místo children , pokud vnode obsahuje textový uzel jako svého jediného potomka. Děje se tak z důvodů výkonu. Komponentní vnode nikdy nepoužívají vlastnost text , i když mají textový uzel jako jediného potomka. |
dom | Element? | Ukazuje na element, který odpovídá vnode. Tato vlastnost je undefined v metodě životního cyklu oninit . Ve fragmentech a trusted HTML vnode dom ukazuje na první element v rozsahu. |
domSize | Number? | Nastavuje se pouze ve fragmentech a trusted HTML vnode a u ostatních typů je undefined . Definuje počet DOM elementů, které vnode reprezentuje (počínaje elementem odkazovaným vlastností dom ). |
state | Object? | Objekt, který je trvalý mezi překresleními. Je poskytován jádrem enginu, když je potřeba. V jednoduchých objektových komponentních vnode dědí state prototypicky z objektu komponenty. V komponentních vnode založených na třídách je to instance třídy. V closure komponentách je to objekt, který vrací closure. |
events | Object? | Objekt, který je trvalý mezi překresleními a který ukládá obslužné rutiny událostí, aby je bylo možné odebrat pomocí DOM API. Vlastnost events je undefined , pokud nejsou definovány žádné obslužné rutiny událostí. Tuto vlastnost používá pouze interně Mithril.js, nepoužívejte ani neupravujte. |
instance | Object? | Pro komponenty, úložné místo pro hodnotu vrácenou view . Tuto vlastnost používá pouze interně Mithril.js, nepoužívejte ani neupravujte. |
Typy Vnode
Vlastnost tag
vnode určuje jeho typ. Existuje pět typů vnode:
Typ Vnode | Příklad | Popis |
---|---|---|
Element | {tag: "div"} | Reprezentuje DOM element. |
Fragment | {tag: "[", children: []} | Reprezentuje seznam DOM elementů, jejichž rodičovský DOM element může také obsahovat další elementy, které nejsou ve fragmentu. Při použití pomocné funkce m() lze fragmentové vnode vytvořit pouze vnořením polí do parametru children funkce m() . m("[") nevytvoří platný fragment. |
Text | {tag: "#", children: ""} | Reprezentuje DOM textový uzel. |
Trusted HTML | {tag: "<", children: "<br>"} | Reprezentuje seznam DOM elementů z HTML řetězce. |
Komponenta | {tag: ExampleComponent} | Pokud je tag JavaScriptový objekt s metodou view , vnode reprezentuje DOM generovaný vykreslením komponenty. |
Všechno ve virtuálním DOM stromu je vnode, včetně textových uzlů. Utilita m()
automaticky normalizuje svůj argument children
a převádí řetězce na textové vnode a vnořená pole na fragmentové vnode.
Pouze názvy tagů a komponent mohou být prvním argumentem funkce m()
. Jinými slovy, [
, #
a <
nejsou platné argumenty selector
pro m()
. Trusted HTML vnode lze vytvořit pomocí m.trust()
Monomorfní třída
Modul mithril/render/vnode
je používán Mithril.js k generování všech vnode. To zajišťuje, že moderní JavaScriptové enginy mohou optimalizovat porovnávání virtuálního DOM tím, že vždy kompilují vnode do stejné skryté třídy.
Při vytváření knihoven, které emitují vnode, byste měli použít tento modul namísto psaní čistých JavaScriptových objektů, abyste zajistili vysokou úroveň výkonu vykreslování.
Vyvarujte se anti-vzorům
Vyhněte se opětovnému použití vnode
Vnode mají reprezentovat stav DOM v určitém časovém okamžiku. Vykreslovací jádro Mithril.js předpokládá, že opakovaně použitý vnode je nezměněn, takže úprava vnode, který byl použit v předchozím vykreslení, povede k nedefinovanému chování.
Je možné znovu použít vnode na místě, abyste se vyhnuli diffu, ale lepší je použít onbeforeupdate
.
Vyhněte se předávání dat modelu přímo komponentám prostřednictvím atributů
Vlastnost key
se může objevit ve vašem datovém modelu způsobem, který je v konfliktu s logikou klíčů Mithril.js. Váš model může být také proměnlivá instance s metodou, která sdílí název s metodou životního cyklu, jako je onupdate
nebo onremove
. Například model může používat vlastnost key
k reprezentaci přizpůsobitelného barevného klíče. Když se to změní, může to vést k tomu, že komponenty obdrží nesprávná data, neočekávaně změní pozice nebo jiné neočekávané, nežádoucí chování. Místo toho jej předejte jako atribut, aby jej Mithril.js neinterpretoval chybně (a abyste jej stále mohli potenciálně mutovat nebo později volat prototypové metody na něm):
// Datový model
var users = [
{ id: 1, name: 'John', key: 'red' },
{ id: 2, name: 'Mary', key: 'blue' },
];
// Později...
users[0].key = 'yellow';
// VYHNOUT SE
users.map(function (user) {
// Komponenta pro Johna bude zničena a znovu vytvořena
return m(UserComponent, user);
});
// PREFEROVAT
users.map(function (user) {
// Klíč je konkrétně extrahován: datový model má svou vlastní vlastnost
return m(UserComponent, { key: user.id, model: user });
});
Vyhněte se příkazům v metodách view
JavaScriptové příkazy v metodách view často vyžadují změnu přirozeně vnořené struktury HTML stromu, což činí kód složitějším a méně čitelným.
// VYHNOUT SE
var BadListComponent = {
view: function (vnode) {
var list = [];
for (var i = 0; i < vnode.attrs.items.length; i++) {
list.push(m('li', vnode.attrs.items[i]));
}
return m('ul', list);
},
};
Místo toho preferujte používání JavaScriptových výrazů, jako je ternární operátor pro podmíněné vykreslování a metody Array pro práci se seznamy.
// PREFEROVAT
var BetterListComponent = {
view: function (vnode) {
return m(
'ul',
vnode.attrs.items.map(function (item) {
return m('li', item);
})
);
},
};