Articles

bitsofcode

Ostatnio pisałem o tym, czym dokładnie są DOM i shadow DOM i czym się różnią. Podsumowując, Obiektowy Model Dokumentu jest obiektową reprezentacją dokumentu HTML i interfejsem do manipulowania tym obiektem. Shadow DOM może być postrzegany jako „lajtowa” wersja DOM. Jest to również obiektowa reprezentacja elementów HTML, ale nie pełnego, samodzielnego dokumentu. Zamiast tego, shadow DOM pozwala nam rozdzielić nasz DOM na mniejsze, zamknięte kawałki, które mogą być używane w dokumentach HTML.

Innym podobnym terminem, z którym mogłeś się zetknąć, jest „wirtualny DOM”. Chociaż koncepcja ta istnieje już od kilku lat, stała się bardziej popularna dzięki wykorzystaniu jej w frameworku React. W tym artykule omówię dokładnie, czym jest wirtualny DOM, jak różni się od oryginalnego DOM i jak jest używany.

Dlaczego potrzebujemy wirtualnego DOM?

Aby zrozumieć, dlaczego powstała koncepcja wirtualnego DOM, wróćmy do oryginalnego DOM. Jak już wspomniałem, DOM składa się z dwóch części – obiektowej reprezentacji dokumentu HTML oraz API do manipulowania tym obiektem.

Na przykład, weźmy ten prosty dokument HTML z nieuporządkowaną listą i jednym elementem listy.

<!doctype html><html lang="en"> <head></head> <body> <ul class="list"> <li class="list__item">List item</li> </ul> </body></html>

Ten dokument może być reprezentowany jako następujące drzewo DOM:

  • html
    • head lang=”en”
    • body
      • ul class=”list”
        • li class=”list__item”
            .

          • „Element listy”
          • .

Załóżmy, że chcemy zmodyfikować zawartość pierwszego elementu listy na "List item one", a także dodać drugi element listy. Aby to zrobić, będziemy musieli użyć interfejsów API DOM, aby znaleźć elementy, które chcemy zaktualizować, utworzyć nowe elementy, dodać atrybuty i zawartość, a następnie zaktualizować same elementy DOM.

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 nie został do tego stworzony…

Kiedy pierwsza specyfikacja DOM została wydana w 1998 roku, budowaliśmy i zarządzaliśmy stronami internetowymi w zupełnie inny sposób. Znacznie mniej polegaliśmy na API DOM, aby tworzyć i aktualizować zawartość strony tak często, jak robimy to dzisiaj.

Proste metody, takie jak document.getElementsByClassName() są w porządku do wykorzystania na małą skalę, ale jeśli aktualizujemy wiele elementów na stronie co kilka sekund, może to zacząć stawać się naprawdę kosztowne, aby stale odpytywać i aktualizować DOM.

Nawet dalej, z powodu sposobu, w jaki API są skonfigurowane, zwykle łatwiej jest wykonać droższe operacje, w których aktualizujemy większe części dokumentu, niż znaleźć i zaktualizować konkretne elementy. Wracając do naszego przykładu z listą, pod pewnymi względami łatwiej jest zastąpić całą listę nieuporządkowaną nową niż zmodyfikować konkretne elementy.

const list = document.getElementsByClassName("list");list.innerHTML = `<li class="list__item">List item one</li><li class="list__item">List item two</li>`;

W tym konkretnym przykładzie różnica wydajności między metodami jest prawdopodobnie nieistotna. Jednak wraz ze wzrostem rozmiaru strony internetowej ważniejsze staje się wybieranie i aktualizowanie tylko tego, co jest potrzebne.

… ale wirtualny DOM był!

Wirtualny DOM został stworzony, aby rozwiązać te problemy związane z koniecznością częstego aktualizowania DOM w bardziej wydajny sposób. W przeciwieństwie do DOM lub shadow DOM, wirtualny DOM nie jest oficjalną specyfikacją, ale raczej nową metodą interakcji z DOM.

Wirtualny DOM może być postrzegany jako kopia oryginalnego DOM. Ta kopia może być często manipulowana i aktualizowana, bez użycia interfejsów API DOM. Po dokonaniu wszystkich aktualizacji w wirtualnym DOM, możemy przyjrzeć się, jakie konkretne zmiany należy wprowadzić w oryginalnym DOM i dokonać ich w ukierunkowany i zoptymalizowany sposób.

Jak wygląda wirtualny DOM?

Nazwa „wirtualny DOM” ma tendencję do dodawania tajemniczości temu, czym właściwie jest ta koncepcja. W rzeczywistości wirtualny DOM jest po prostu zwykłym obiektem Javascript.

Powróćmy do drzewa DOM, które utworzyliśmy wcześniej:

  • html
    • head lang=”en”
    • body
      • ul class=”list”
        • li class=”list__item”
          • „Element listy”

To drzewo może być również reprezentowane jako obiekt Javascript.

const vdom = { tagName: "html", children: } // end ul ] } // end body ]} // end html

Możemy myśleć o tym obiekcie jako o naszym wirtualnym DOM. Podobnie jak oryginalny DOM, jest on obiektową reprezentacją naszego dokumentu HTML. Ale ponieważ jest to zwykły obiekt Javascript, możemy nim swobodnie i często manipulować, nie dotykając rzeczywistego DOM, dopóki nie zajdzie taka potrzeba.

Zamiast używać jednego obiektu dla całego obiektu, częściej pracuje się z małymi sekcjami wirtualnego DOM. Na przykład, możemy pracować nad komponentem list, który odpowiadałby naszemu elementowi listy nieuporządkowanej.

const list = { tagName: "ul", attributes: { "class": "list" }, children: };

Pod maską wirtualnego DOM

Teraz, gdy zobaczyliśmy, jak wygląda wirtualny DOM, jak działa, aby rozwiązać problemy z wydajnością i użytecznością DOM?

Jak wspomniałem, możemy użyć wirtualnego DOM do wyodrębnienia konkretnych zmian, które muszą być dokonane w DOM i dokonać tych konkretnych aktualizacji samodzielnie. Wróćmy do naszego przykładu listy nieuporządkowanej i dokonajmy tych samych zmian, które zrobiliśmy używając DOM API.

Pierwszą rzeczą, którą byśmy zrobili, jest wykonanie kopii wirtualnego DOM, zawierającej zmiany, które chcemy wprowadzić. Ponieważ nie musimy używać DOM API, możemy właściwie po prostu utworzyć nowy obiekt.

const copy = { tagName: "ul", attributes: { "class": "list" }, children: };

Ten copy jest używany do tworzenia tak zwanego „diff” pomiędzy oryginalnym wirtualnym DOM, w tym przypadku list, a zaktualizowanym. Dyfuzja może wyglądać mniej więcej tak:

const diffs = 

Ta dyfuzja dostarcza instrukcji, jak zaktualizować rzeczywisty DOM. Gdy wszystkie różnice zostaną zebrane, możemy wprowadzić zmiany w DOM w partiach, dokonując tylko tych aktualizacji, które są potrzebne.

Na przykład możemy zapętlić każdą z różnic i albo dodać nowe dziecko, albo zaktualizować stare, w zależności od tego, co określa różnica.

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); }})

Zauważ, że jest to naprawdę uproszczona i okrojona wersja tego, jak wirtualny DOM mógłby działać i jest wiele przypadków, których tutaj nie uwzględniłem.

Wirtualny DOM i frameworki

Najczęstszym sposobem pracy z wirtualnym DOM-em jest praca z frameworkiem, zamiast bezpośredniego połączenia z nim, jak pokazałem w powyższym przykładzie.

Frameworki takie jak React i Vue używają koncepcji wirtualnego DOM-u do bardziej wydajnych aktualizacji DOM-u. Na przykład, nasz komponent list może być napisany w React w następujący sposób.

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);

Jeśli chcemy zaktualizować naszą listę, moglibyśmy po prostu przepisać cały szablon listy i wywołać ReactDOM.render() ponownie, przekazując nową 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);

Ponieważ React używa wirtualnego DOM, nawet jeśli ponownie renderujemy cały szablon, aktualizowane są tylko te części, które faktycznie się zmieniają. Jeśli spojrzymy na nasze narzędzia deweloperskie, gdy nastąpi zmiana, zobaczymy konkretne elementy i konkretne części elementów, które się zmieniają.

DOM vs wirtualny DOM

Podsumowując, wirtualny DOM jest narzędziem, które umożliwia nam interfejs z elementami DOM w łatwiejszy i bardziej wydajny sposób. Jest to obiektowa reprezentacja DOM w języku Javascript, którą możemy modyfikować tak często, jak tego potrzebujemy. Zmiany dokonywane w tym obiekcie są następnie zestawiane, a modyfikacje rzeczywistego DOM są ukierunkowane i dokonywane rzadziej.