bitsofcode
Am scris de curând despre ce anume sunt DOM și DOM-ul virtual și care sunt diferențele dintre ele. Pentru a recapitula, Document Object Model este o reprezentare bazată pe obiecte a unui document HTML și o interfață pentru manipularea acelui obiect. Shadow DOM poate fi considerat ca o versiune „lite” a DOM. Este, de asemenea, o reprezentare bazată pe obiecte a elementelor HTML, dar nu a unui document de sine stătător complet. În schimb, shadow DOM ne permite să separăm DOM-ul nostru în fragmente mai mici, încapsulate, care pot fi utilizate în toate documentele HTML.
Un alt termen similar pe care este posibil să-l fi întâlnit este „DOM virtual”. Deși conceptul există de câțiva ani, acesta a devenit mai popular prin utilizarea sa în cadrul React. În acest articol, voi acoperi exact ce este DOM-ul virtual, cum diferă de DOM-ul original și cum este utilizat.
De ce avem nevoie de un DOM virtual?
Pentru a înțelege de ce a apărut conceptul de DOM virtual, haideți să revizuim DOM-ul original. După cum am menționat, există două părți în DOM – reprezentarea bazată pe obiecte a documentului HTML și API-ul pentru manipularea acelui obiect.
De exemplu, să luăm acest simplu document HTML cu o listă neordonată și un singur element de listă.
<!doctype html><html lang="en"> <head></head> <body> <ul class="list"> <li class="list__item">List item</li> </ul> </body></html>
Acest document poate fi reprezentat ca următorul arbore DOM:
- html
- head lang=”en”
- body
- ul class=”list”
- li class=”list__item”
- .
- „List item”
.
- li class=”list__item”
- ul class=”list”
Să spunem că dorim să modificăm conținutul primului element de listă în "List item one"
și să adăugăm și un al doilea element de listă. Pentru a face acest lucru, va trebui să folosim API-urile DOM pentru a găsi elementele pe care dorim să le actualizăm, să creăm noile elemente, să adăugăm atribute și conținut, apoi, în cele din urmă, să actualizăm elementele DOM în sine.
const listItemOne = document.getElementsByClassName("list__item");listItemOne.textContent = "List item one";const list = document.getElementsByClassName("list");const listItemTwo = document.createElement("li");listItemTwo.classList.add("list__item");listItemTwo.textContent = "List item two";list.appendChild(listItemTwo);
DOM-ul nu a fost făcut pentru asta…
Când prima specificație pentru DOM a fost publicată în 1998, construiam și gestionam paginile web în mod foarte diferit. Exista mult mai puțină încredere în API-urile DOM pentru a crea și actualiza conținutul paginii la fel de frecvent cum o facem astăzi.
Metodele simple, cum ar fi document.getElementsByClassName()
, sunt bune de utilizat la scară mică, dar dacă actualizăm mai multe elemente dintr-o pagină la fiecare câteva secunde, poate începe să devină foarte costisitor să interogăm și să actualizăm în mod constant DOM-ul.
Chiar și mai mult, din cauza modului în care sunt configurate API-urile, este de obicei mai simplu să efectuăm operațiuni mai costisitoare în care actualizăm părți mai mari ale documentului decât să găsim și să actualizăm elementele specifice. Revenind la exemplul nostru de listă, este într-un fel mai ușor să înlocuim întreaga listă neordonată cu una nouă decât să modificăm elementele specifice.
const list = document.getElementsByClassName("list");list.innerHTML = `<li class="list__item">List item one</li><li class="list__item">List item two</li>`;
În acest exemplu particular, diferența de performanță dintre metode este probabil nesemnificativă. Cu toate acestea, pe măsură ce dimensiunea paginii web crește, devine mai important să se selecteze și să se actualizeze doar ceea ce este necesar.
… dar DOM-ul virtual a fost!
DOM-ul virtual a fost creat pentru a rezolva aceste probleme legate de necesitatea de a actualiza frecvent DOM-ul într-un mod mai performant. Spre deosebire de DOM sau de DOM umbră, DOM virtual nu este o specificație oficială, ci mai degrabă o nouă metodă de interfațare cu DOM.
Un DOM virtual poate fi considerat ca o copie a DOM-ului original. Această copie poate fi manipulată și actualizată frecvent, fără a utiliza API-urile DOM. Odată ce toate actualizările au fost făcute în DOM-ul virtual, putem analiza ce modificări specifice trebuie făcute în DOM-ul original și le putem face într-un mod țintit și optimizat.
Cum arată un DOM virtual?
Numele „DOM virtual” tinde să sporească misterul cu privire la ceea ce este de fapt acest concept. De fapt, un DOM virtual este doar un obiect Javascript obișnuit.
Să revizuim arborele DOM pe care l-am creat mai devreme:
- html
- head lang=”en”
- body
- ul class=”list”
- li class=”list__item”
- „List item”
- li class=”list__item”
- ul class=”list”
Acest arbore poate fi reprezentat, de asemenea, ca un obiect Javascript.
const vdom = { tagName: "html", children: } // end ul ] } // end body ]} // end html
Ne putem gândi la acest obiect ca fiind DOM-ul nostru virtual. Ca și DOM-ul original, este o reprezentare bazată pe obiecte a documentului nostru HTML. Dar, deoarece este un simplu obiect Javascript, îl putem manipula liber și frecvent fără să atingem DOM-ul real până când avem nevoie.
În loc să folosim un obiect pentru întregul obiect, este mai frecvent să lucrăm cu mici secțiuni din DOM-ul virtual. De exemplu, putem lucra la o componentă list
, care ar corespunde elementului nostru de listă neordonată.
const list = { tagName: "ul", attributes: { "class": "list" }, children: };
Sub capota DOM-ului virtual
Acum că am văzut cum arată un DOM virtual, cum funcționează acesta pentru a rezolva problemele de performanță și utilizare ale DOM-ului?
După cum am menționat, putem folosi DOM-ul virtual pentru a individualiza modificările specifice care trebuie făcute în DOM și pentru a face doar acele actualizări specifice. Să ne întoarcem la exemplul nostru de listă neordonată și să facem aceleași modificări pe care le-am făcut folosind API-ul DOM.
Primul lucru pe care l-am face este să facem o copie a DOM-ului virtual, care să conțină modificările pe care dorim să le facem. Din moment ce nu trebuie să folosim API-ul DOM, putem de fapt să creăm pur și simplu un obiect nou cu totul.
const copy = { tagName: "ul", attributes: { "class": "list" }, children: };
Acest copy
este folosit pentru a crea ceea ce se numește „diff” între DOM-ul virtual original, în acest caz list
, și cel actualizat. Un diff ar putea arăta cam așa:
const diffs =
Acest diff oferă instrucțiuni pentru modul de actualizare a DOM real. Odată ce toate diff-urile sunt colectate, putem face modificări pe loturi la DOM, efectuând doar actualizările care sunt necesare.
De exemplu, am putea trece în buclă prin fiecare diff și fie să adăugăm un nou copil, fie să actualizăm unul vechi, în funcție de ceea ce specifică diff-ul.
const domElement = document.getElementsByClassName("list");diffs.forEach((diff) => { const newElement = document.createElement(diff.newNode.tagName); /* Add attributes ... */ if (diff.oldNode) { // If there is an old version, replace it with the new version domElement.replaceChild(diff.newNode, diff.index); } else { // If no old version exists, create a new node domElement.appendChild(diff.newNode); }})
Rețineți că aceasta este o versiune foarte simplificată și desprinsă din modul în care ar putea funcționa un DOM virtual și există o mulțime de cazuri pe care nu le-am acoperit aici.
DOM-ul virtual și cadrele de lucru
Este mai frecvent să se lucreze cu DOM-ul virtual prin intermediul unui cadru de lucru, decât să se interfațeze direct cu acesta, așa cum am arătat în exemplul de mai sus.
Cadre de lucru precum React și Vue folosesc conceptul de DOM virtual pentru a face actualizări mai performante ale DOM-ului. De exemplu, componenta noastră list
poate fi scrisă în React în felul următor.
import React from 'react';import ReactDOM from 'react-dom';const list = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item"));ReactDOM.render(list, document.body);
Dacă am dori să actualizăm lista noastră, am putea doar să rescriem întregul șablon de listă și să apelăm din nou ReactDOM.render()
, trecând noua listă.
const newList = React.createElement("ul", { className: "list" }, React.createElement("li", { className: "list__item" }, "List item one"), React.createElement("li", { className: "list__item" }, "List item two"););setTimeout(() => ReactDOM.render(newList, document.body), 5000);
Pentru că React folosește DOM virtual, chiar dacă redau întregul șablon, sunt actualizate doar părțile care se schimbă efectiv. Dacă ne uităm la instrumentele noastre de dezvoltare atunci când are loc modificarea, vom vedea elementele specifice și părțile specifice ale elementelor care se schimbă.
DOM-ul vs. DOM-ul virtual
Pentru a recapitula, DOM-ul virtual este un instrument care ne permite să interfațăm cu elementele DOM într-un mod mai ușor și mai performant. Este o reprezentare obiectuală Javascript a DOM, pe care o putem modifica atât de des cât avem nevoie. Modificările aduse acestui obiect sunt apoi colaționate, iar modificările aduse DOM-ului real sunt direcționate și efectuate mai rar.
.