что такое объект dom
Введение
Что такое Объектная Модель Документа (DOM)?
Объектная Модель Документа (DOM) – это программный интерфейс (API) для HTML и XML документов. DOM предоставляет структурированное представление документа и определяет то, как эта структура может быть доступна из программ, которые могут изменять содержимое, стиль и структуру документа. Представление DOM состоит из структурированной группы узлов и объектов, которые имеют свойства и методы. По существу, DOM соединяет веб-страницу с языками описания сценариев либо языками программирования.
Веб-страница – это документ. Документ может быть представлен как в окне браузера, так и в самом HTML-коде. В любом случае, это один и тот же документ. DOM предоставляет другой способ представления, хранения и управления этого документа. DOM полностью поддерживает объектно-ориентированное представление веб-страницы, делая возможным её изменение при помощи языка описания сценариев наподобие JavaScript.
Стандарты W3C DOM и WHATWG DOM формируют основы DOM, реализованные в большинстве современных браузеров. Многие браузеры предлагают расширения за пределами данного стандарта, поэтому необходимо проверять работоспособность тех или иных возможностей DOM для каждого конкретного браузера.
Например: стандарт DOM описывает, что метод getElementsByTagName в коде, указанном ниже, должен возвращать список всех элементов
DOM и JavaScript
Вначале JavaScript и DOM были тесно связаны, но впоследствии они развились в различные сущности. Содержимое страницы хранится в DOM и может быть доступно и изменяться с использованием JavaScript, поэтому мы можем записать это в виде приблизительного равенства:
API (веб либо XML страница) = DOM + JS (язык описания скриптов)
DOM спроектирован таким образом, чтобы быть независимым от любого конкретного языка программирования, обеспечивая структурное представление документа согласно единому и последовательному API. Хотя мы всецело сфокусированы на JavaScript в этой справочной документации, реализация DOM может быть построена для любого языка, как в следующем примере на Python:
Для подробной информации о том, какие технологии участвуют в написании JavaScript для веб, смотрите обзорную статью JavaScript technologies overview.
Каким образом доступен DOM?
Вы не должны делать ничего особенного для работы с DOM. Различные браузеры имеют различную реализацию DOM, эти реализации показывают различную степень соответствия с действительным стандартом DOM (это тема, которую мы пытались не затрагивать в данной документации), но каждый браузер использует свой DOM, чтобы сделать веб страницы доступными для взаимодействия с языками сценариев.
При создании сценария с использованием элемента
Что такое DOM и зачем он нужен?
На этом уроке мы рассмотрим, что такое DOM, зачем он нужен, а также то, как он строится.
Что такое DOM
DOM – это объектная модель документа, которую браузер создаёт в памяти компьютера на основании HTML-кода, полученного им от сервера.
Если сказать по-простому, то HTML-код – это текст страницы, а DOM – это набор связанных объектов, созданных браузером при парсинге её текста.
В Chrome исходный код страницы, который получает браузер, можно посмотреть во вкладке «Source» на панели «Инструменты веб-разработчика».
В Chrome инструмента, с помощью которого можно было бы посмотреть созданное им DOM-дерево нет. Но есть представление этого DOM-дерева в виде HTML-кода, оно доступно на вкладке «Elements». С таким представлением DOM веб-разработчику, конечно, намного удобнее работать. Поэтому инструмента, который DOM представлял бы в виде древовидной структуры нет.
Для работы с DOM в большинстве случаев используется JavaScript, т.к. на сегодняшний день это единственный язык программирования, скрипты на котором могут выполняться в браузере.
Зачем нам нужен DOM API? Он нам нужен для того, чтобы мы могли с помощью JavaScript изменять страницу на «лету», т.е. делать её динамической и интерактивной.
DOM API предоставляет нам (разработчикам) огромное количество методов, с помощью которых мы можем менять всё что есть на странице, а также взаимодействовать с пользователем. Т.е. данный программный интерфейс позволяет нам создавать сложные интерфейсы, формы, выполнять обработку действий пользователей, добавлять и удалять различные элементы на странице, изменять их содержимое, свойства (атрибуты), и многое другое.
Сейчас в вебе практически нет сайтов в сценариях которых отсутствовала бы работа с DOM.
Из чего состоит HTML-код страницы
Перед тем, как перейти к изучению объектной модели документа необходимо сначала вспомнить, что из себя представляет исходный код веб-страницы (HTML-документа).
Исходный код веб-страницы состоит из тегов, атрибутов, комментариев и текста. Теги — это базовая синтаксическая конструкция HTML. Большинство из них являются парными. В этом случае один из них является открывающим, а другой – закрывающим. Одна такая пара тегов образует HTML-элемент. HTML-элементы могут иметь дополнительные параметры – атрибуты.
В документе для создания определённой разметки одни элементы находятся внутри других. В результате HTML-документ можно представить как множество вложенных друг в друга HTML-элементов.
В качестве примера рассмотрим следующий HTML код:
Теперь рассмотрим, как браузер на основании HTML-кода строит DOM-дерево.
Как строится DOM-дерево документа
Как уже было описано выше браузер строит дерево на основе HTML-элементов и других сущностей исходного кода страницы. При выполнении этого процесса он учитывает вложенность элементов друг в друга.
В результате браузер полученное DOM-дерево использует не только в своей работе, но также предоставляет нам API для удобной работы с ним через JavaScript.
При строительстве DOM браузер создаёт из HTML-элементов, текста, комментариев и других сущностей этого языка объекты (узлы DOM-дерева).
В большинстве случаев веб-разработчиков интересуют только объекты (узлы), образованные из HTML-элементов.
При этом браузер не просто создаёт объекты из HTML-элементов, а также связывает их между собой определёнными связями в зависимости от того, как каждый из них относится к другому в коде.
Элементы, которые находятся непосредственно в некотором элементе являются по отношению к нему детьми. А он для каждого из них является родителем. Кроме этого, все эти элементы по отношению друг к другу являются сиблингами (братьями).
Чтобы получить DOM-дерево так как его строит браузер, необходимо просто «выстроить» все элементы в зависимости от их отношения друг к другу.
Создание DOM-дерева выполняется сверху вниз.
При этом корнем DOM-дерева всегда является сам документ (узел document ). Далее дерево строится в зависимости от структуры HTML кода.
Например, HTML-код, который мы рассматривали выше будет иметь следующее DOM-дерево:
Форум
Справочник
Введение. DOM в примерах.
Простейший DOM
Построим, для начала, дерево DOM для следующего документа.
Пример посложнее
Рассмотрим теперь более жизненную страничку:
А вот так выглядит дерево, если изобразить его прямо на HTML-страничке:
Кстати, дерево на этом рисунке не учитывает текст, состоящий из одних пробельных символов. Например, такой текстовый узел должен идти сразу после
- . DOM, не содержащий таких «пустых» узлов, называют «нормализованным».
Пример с атрибутами и DOCTYPE
Рассмотрим чуть более сложный документ.
Однако, в веб-программировании в эти дебри обычно не лезут, и считают атрибуты просто свойствами DOM-узла, которые, как мы увидим в дальнейшем, можно устанавливать и менять по желанию программиста.
Вообще-то это секрет, но DOCTYPE тоже является DOM-узлом, и находится в дереве DOM слева от HTML (на рисунке этот факт скрыт).
Нормализация в различных браузерах
При разборе HTML Internet Explorer сразу создает нормализованный DOM, в котором не создаются узлы из пустого текста.
На рисунке для краткости текстовые узлы обозначены просто решеткой. У body вместо 3 появилось 7 детей.
Opera тоже имеет чем похвастаться. Она может добавить лишний пустой элемент «просто от себя».
У меня получается 3 для IE, 7 для Firefox и 8 (!?) для Opera.
На практике эта несовместимость не создает больших проблем, но нужно о ней помнить. Например, разница может проявить себя в случае перебора узлов дерева.
Возможности, которые дает DOM
Зачем, кроме красивых рисунков, нужна иерархическая модель DOM?
Каждый DOM-элемент является объектом и предоставляет свойства для манипуляции своим содержимым, для доступа к родителям и потомкам.
В современных скриптах этот метод почти не используется, случаи его правильного применения можно пересчитать по пальцам.
Разберем подробнее способы доступа и свойства элементов DOM.
Доступ к элементам
Начнем с вершины дерева.
document.documentElement
document.body
Типы DOM-элементов
У каждого элемента в DOM-модели есть тип. Его номер хранится в атрибуте elem.nodeType
Всего в DOM различают 12 типов элементов.
Остальные типы в javascript программировании не используются.
Пример
Например, вот так выглядел бы в браузере документ из примера выше, если каждый видимый элемент обвести рамкой с цифрой nodeType в правом верхнем углу.
Дочерние элементы
Этих свойств вполне хватает для удобного обращения к соседям.
Свойства элементов
Рассмотрим здесь еще некоторые (не все) свойства элементов, полезные при работе с DOM.
tagName
Атрибут есть у элементов-тегов и содержит имя тега в верхнем регистре, только для чтения.
style
Это свойство управляет стилем. Оно аналогично установке стиля в CSS.
Например, можно установить element.style.width :
Исходный код этой кнопки:
Например, для установки свойства z-index в 1000, нужно поставить:
innerHTML
Когда-то это свойство поддерживалось только в IE. Теперь его поддерживают все современные браузеры.
Оно содержит весь HTML-код внутри узла, и его можно менять.
Свойство innerHTML применяется, в основном, для динамического изменения содержания страницы, например:
className
Это свойство задает класс элемента. Оно полностью аналогично html-атрибуту «class».
Шпаргалка по JS-методам для работы с DOM
Основные источники
Введение
JavaScript предоставляет множество методов для работы с Document Object Model или сокращенно DOM (объектной моделью документа): одни из них являются более полезными, чем другие; одни используются часто, другие почти никогда; одни являются относительно новыми, другие признаны устаревшими.
Я постараюсь дать вам исчерпывающее представление об этих методах, а также покажу парочку полезных приемов, которые сделают вашу жизнь веб-разработчика немного легче.
Размышляя над подачей материала, я пришел к выводу, что оптимальным будет следование спецификациям с промежуточными и заключительными выводами, сопряженными с небольшими лирическими отступлениями.
Сильно погружаться в теорию мы не будем. Вместо этого, мы сосредоточимся на практической составляющей.
Для того, чтобы получить максимальную пользу от данной шпаргалки, пишите код вместе со мной и внимательно следите за тем, что происходит в консоли инструментов разработчика и на странице.
Вот как будет выглядеть наша начальная разметка:
У нас есть список ( ul ) с тремя элементами ( li ). Список и каждый элемент имеют идентификатор ( id ) и CSS-класс ( class ). id и class — это атрибуты элемента. Существует множество других атрибутов: одни из них являются глобальными, т.е. могут добавляться к любому элементу, другие — локальными, т.е. могут добавляться только к определенным элементам.
Мы часто будем выводить данные в консоль, поэтому создадим такую «утилиту»:
Миксин NonElementParentNode
Данный миксин предназначен для обработки (браузером) родительских узлов, которые не являются элементами.
В чем разница между узлами (nodes) и элементами (elements)? Если кратко, то «узлы» — это более общее понятие, чем «элементы». Узел может быть представлен элементом, текстом, комментарием и т.д. Элемент — это узел, представленный разметкой (HTML-тегами (открывающим и закрывающим) или, соответственно, одним тегом).
Небольшая оговорка: разумеется, мы могли бы создать список и элементы программным способом.
Для создания элементов используется метод createElement(tag) объекта Document :
Одним из основных способов получения элемента (точнее, ссылки на элемент) является метод getElementById(id) объекта Document :
Почему идентификаторы должны быть уникальными в пределах приложения (страницы)? Потому что элемент с id становится значением одноименного свойства глобального объекта window :
Миксин ParentNode
Данный миксин предназначен для обработки родительских элементов (предков), т.е. элементов, содержащих одного и более потомка (дочерних элементов).
Такая структура называется коллекцией HTML и представляет собой массивоподобный объект (псевдомассив). Существует еще одна похожая структура — список узлов (NodeList ).
Для дальнейших манипуляций нам потребуется периодически создавать новые элементы, поэтому создадим еще одну утилиту:
Наша утилита принимает 4 аргумента: идентификатор, текст, название тега и CSS-класс. 2 аргумента (тег и класс) имеют значения по умолчанию. Функция возвращает готовый к работе элемент. Впоследствии, мы реализуем более универсальный вариант данной утилиты.
Одной из интересных особенностей HTMLCollection является то, что она является «живой», т.е. элементы, возвращаемые по ссылке, и их количество обновляются автоматически. Однако, эту особенность нельзя использовать, например, для автоматического добавления обработчиков событий.
Создадим универсальную утилиту для получения элементов:
Наша утилита принимает 3 аргумента: CSS-селектор, родительский элемент и индикатор количества элементов (один или все). 2 аргумента (предок и индикатор) имеют значения по умолчанию. Функция возвращает либо один, либо все элементы (в виде обычного массива), совпадающие с селектором, в зависимости от значения индикатора:
Миксин NonDocumentTypeChildNode
Миксин ChildNode
Данный миксин предназначен для обработки дочерних элементов, т.е. элементов, являющихся потомками других элементов.
Интерфейс Node
Данный интерфейс предназначен для обработки узлов.
Интерфейс Document
Фрагменты позволяют избежать создания лишних элементов. Они часто используются при работе с разметкой, скрытой от пользователя с помощью тега template (метод cloneNode() возвращает DocumentFragment ).
createTextNode(data) — создает текст
createComment(data) — создает комментарий
importNode(existingNode, deep) — создает новый узел на основе существующего
Интерфейсы NodeIterator и TreeWalker
Интерфейсы NodeIterator и TreeWalker предназначены для обхода (traverse) деревьев узлов. Я не сталкивался с примерами их практического использования, поэтому ограничусь парочкой примеров:
Интерфейс Element
Данный интерфейс предназначен для обработки элементов.
Работа с classList
Работа с атрибутами
insertAdjacentElement(where, newElement) — универсальный метод для вставки новых элементов перед/в начало/в конец/после текущего элемента. Аргумент where определяет место вставки. Возможные значения:
insertAdjacentText(where, data) — универсальный метод для вставки текста
Text — конструктор для создания текста
Comment — конструктор для создания комментария
Объект Document
Свойства объекта location :
reload() — перезагружает текущую локацию
replace() — заменяет текущую локацию на новую
title — заголовок документа
head — метаданные документа
body — тело документа
images — псевдомассив ( HTMLCollection ), содержащий все изображения, имеющиеся в документе
Следующие методы и свойство считаются устаревшими:
Миксин InnerHTML
Геттер/сеттер innerHTML позволяет извлекать/записывать разметку в элемент. Для подготовки разметки удобно пользоваться шаблонными литералами:
Расширения интерфейса Element
Вот несколько полезных ссылок, с которых можно начать изучение этих замечательных инструментов:
Иногда требуется создать элемент на основе шаблонной строки. Как это можно сделать? Вот соответствующая утилита:
Существует более экзотический способ создания элемента на основе шаблонной строки. Он предполагает использование конструктора DOMParser() :
Еще более экзотический, но при этом самый короткий способ предполагает использование расширения для объекта Range — метода createContextualFragment() :
В завершение, как и обещал, универсальная утилита для создания элементов:
Заключение
Разумеется, шпаргалка — это всего лишь карманный справочник, памятка для быстрого восстановления забытого материала, предполагающая наличие определенного багажа знаний.
VDS от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!
DOM, который построил Chrome. Или не построил? Или не Chrome? Или не DOM?
Обычный, теневой, виртуальный, инкрементальный… Как получилось, что простой программный интерфейс доступа к элементам веб-страниц обзавелся таким количеством «родственников»? Чем современные фреймворки не устраивает стандартная объектная модель документа или просто DOM? Что и как на самом деле отрисовывает браузер в процессе рендера веб-страницы?
Всем привет, это Макс Кравец из Holyweb. Помните сцену из Матрицы, в которой один из юных кандидатов в Избранные наставляет Нео: «Не пытайся согнуть ложку. Первое, что ты должен понять — ложки не существует!»? Давайте переформулирую: «Не пытайся изменить DOM. ». А вот о том, что прячется под многоточием, мы сегодня и поговорим.
Фундамент. Как строится веб-страница. CRP
Придется начать с самого начала — разобраться с процессом преобразования исходного HTML в содержимое страницы, который называют Critical Rendering Path (критический путь рендеринга).
Построение дерева DOM
Получив с сервера документ, парсер браузера каждый тег превращает в узел и строит их иерархию
В результате получается дерево узлов (node), или просто DOM-дерево, в котором вложенные элементы представлены в виде дочерних узлов с полным набор атрибутов:
Исторически-лирическое отступление. Когда все только задумывалось, страницы рендерились из статики, каналы связи были неторопливыми, а пользователи — непритязательными. Документ мог грузиться довольно долго и практически не изменялся за время жизни в браузере. Для того, чтобы страница как можно скорее отвечала на действие пользователя, любое изменение DOM автоматически запускало процесс повторного рендеринга страницы.
Но ведь добавление очередного узла в DOM в процессе парсинга — это тоже изменение? Так и есть. Стоит узлу попасть в объектную модель документа — страница перерисовывается. До сих пор! На практике это означает, что документ отрисовывается по частям, браузер даже не дожидается окончания загрузки. Пользователи интернета со стажем помнят, как это выглядело. Ну а сегодня можно в инструментах разработчика поставить качество связи Slow 3G на вкладке network и насладиться загрузкой какой-либо статической страницы.
Нам как разработчикам важен сам факт — любое изменение физического DOM требует перерисовки страницы, а значит — времени и ресурсов.
Построение CSSOM-дерева
Хорошо, верстку мы получили, надо сделать ее красивой. Пробежимся по полученному ранее DOM и добавим каждому узлу соответствующие стили. Говоря языком документации — сформируем CSS object model или CSSOM.
Если в примере выше в файле style.css будет
то соответствующее CSSOM дерево будет выглядеть следующим образом:
Еще одно отступление. CSS недаром называют Cascading Style Sheets — каскадными таблицами стилей. Чтобы применить стиль для конкретного узла, необходимо его посчитать, разобрав полностью всю таблицу стилей документа. Следовательно — на это время процесс рендера нужно приостановить.
CSS является блокирующим ресурсом. Причем не только для верстки, но и для скриптов, которые также вынуждены дожидаться построения CSSOM для того, чтобы начать выполняться.
На практике это означает, что мы должны учитывать время, которое нужно для построения CSSOM при написании своего кода.
Хорошая новость — CSS блокирует рендер только при применении. Стили, указанные с помощью медиа-атрибута для мобильного разрешения, не являются блокирующими (и не разбираются) при рендере десктопной версии и наоборот, а стили для ландшафтной ориентации устройства не участвуют в рендере для портретной.
Плохая новость — изменение размера окна браузера или поворот мобильного из одной ориентацию в другую может вызвать срабатывание медиа-атрибута и блокировку рендера страницы для пересчета CSS. Если забыть про этот нюанс, можно потерять немало времени на поиск ошибок не в том месте.
Запуск JavaScript
Ура, добрались! JavaScript — это наша альфа и омега, именно с его помощью мы делаем веб-страницы интерактивными — такими, к каким привык современный пользователь. Но за счет чего мы это делаем? За счет изменения DOM и стилей.
Стоп, скажете вы! И будете правы — именно стоп. Когда парсер доходит до тега
Велосипед 12000 руб.
В этом примере браузер берет элементы из обычного DOM и отображает их в соответствующих слотах теневого дерева.
Это открывает широкие возможности для создания собственных компонентов. Но как быть, когда хочется расширить возможности стандартных элементов?
Одна из проблем, с которыми приходится сталкиваться при динамическом добавлении CSS-свойств элементу через element.style — невозможно напрямую использовать медиазапросы или определить псевдоклассы и псевдоэлементы. Shadow DOM дает доступ к элементу контейнеру через селектор ::host
Мы «подцепили» условному div-у реакцию на наведение мыши, при этом не создавали новые классы и не изменяли внешние стили.
Остается отметить, что теневых DOM на странице может быть неограниченное количество, а каждому корню теневого DOM должен соответствовать элемент в обычном DOM, который будет являться базовым хостом.
Ограничения Shadow DOM:
создается только с помощью JS, а потому не существует возможности предварительного рендера (SSR). Данное ограничение можно обойти, но это тема для другой статьи
Требует внешнего контроля жизненного цикла компонентов и их инициализации во внешней среде
Из-за использования сайтами политик безопасности, существуют серьезные ограничения на добавление стилей к элементам внутри теневого DOM. Дело в том, что CSP (Content Security Policy) запрещает парсить стили из строки. Обойти это можно отключением политики безопасности, но при разработке виджета для стороннего сайта это, пожалуй, самое плохое решение. Более универсальные решения — создание динамических стилей через element.style или добавление в Shadow DOM внешнего css-файла
создание Shadow DOM, как и любого другое действие, требует выделения дополнительных ресурсов, а потому злоупотреблять им не стоит
отсутствует поддержка в IE
Беремся за второй этаж. Зачем понадобился Virtual DOM?
На самом деле ответ прост и мы его уже знаем — физический DOM устроен так, что любое его изменение автоматически вызывает перерисовку страницы, а это не всегда нужно.
Представьте, что вам нужно выводить на странице список товаров
DOM для такой верстки:
А теперь добавим в список самокат, а дефолтный товар заменим на велосипед. Для этого надо найти в DOM список, создать новый элемент, добавить в него контент, обновить контент в старом элементе списка, после чего обновить сам DOM
При этом каждый раз, когда мы «дергаем» DOM API — запускается алгоритм пересчета изменений и рендера страницы.
Откровенно говоря, проще заменить старый именованный список на новый
В этом варианте мы выполнили всего одно обращение к DOM и единожды отрисовали страницу заново. А значит — выиграли в производительности.
Хочу такой же, но с перламутровыми пуговицами!
Как этого добиться? Сделать «копию» DOM, выполнить все нужные преобразования, и только когда все посчитано — обновить реальный DOM. Поздравляю, мы только что сформулировали основную идею Virtual DOM.
Давайте сформулируем и реализацию. В качестве копии — воспользуемся самым обычным JS-объектом. Для нашего примера со списком продуктов его можно представить как
Первый плюс мы уже получили — нет нужды лишний раз перерисовывать страницу. А значит, уже выиграли в производительности.
Второй плюс вытекает из самого факта того, что мы работаем с объектом. Мы избавились от необходимости постоянно обращаться к громоздкому браузерному API. Ставим еще плюсик в производительность.
Но можно ли еще как-то усовершенствовать идею? Конечно! в DOM у нас складывается весь документ целиком, и его копия — довольно большой объект. Из которого нам в нашем случае нужен всего лишь один компонент!
Давайте сделаем следующий шаг: создадим для каждого компонента свой объект и будем работать с ними, как с некими разделами Virtual DOM.
Мы еще на порядок упростили работу с конкретным компонентом и вновь выиграли в производительности, поскольку взаимодействуем с небольшим объектом, а не с копией всего DOM в целом.
Как работает виртуальный DOM
Вернемся к нашему примеру и проделаем те же операции. Поскольку API браузера нас больше не ограничивает, мы просто создадим новый объект
Остается пройтись циклом по диффам, обновить старые или добавить новые элементы
Создать новый объект, посчитать его разницу с предыдущим и точечно обновить элементы — намного быстрее, чем добавить на страницу новую строку с версткой, вызвав тем самым ее полный пересчет и перерисовку.
Давайте подведем промежуточный итог. Мы сформулировали принципиальную идею отказаться от работы непосредственно с DOM, пришли к тезису что удобнее работать с отдельным объектом для каждого компонента и предусмотрели механизм, который позволяет запустить рендер страницы. Разумеется, это не production версия Virtual DOM, а только объяснение принципа его работы. В зависимости от того, какой фреймворк вы используете (и даже от его версии) — детали реализации могут отличаться.
Virtual DOM — это объектное представления JavaScript, которое позволяет взаимодействовать с элементами DOM более простым и производительным способом. Объект-представление можно изменять так часто, как это необходимо, после завершения вычислений вызывается механизм сравнения изменений. В реальный DOM вносятся только финальные изменения, что происходит намного реже, не требует большого количества обращений к API браузера и, следовательно, повышает производительность.
Для тех, кто готов достроить третий этаж
Дошедшим до этого раздела уже должно быть понятно, почему современному фронтенд-разработчику не стоит пытаться «согнуть ложку», а точнее — пытаться общаться с DOM напрямую. Для этого есть более производительный и удобный Virtual DOM, предоставляемый фреймворками.
Но постойте, браузер-то умеет работать только со своим API, а наши компоненты — только с Virtual DOM. Нужен движок, «переводчик» пожеланий компонентов в инструкции, понятные браузеру. И этот движок мы должны загрузить в браузер вместе с самими компонентами.
Как уменьшить размер бандла?
Проблема в том, что на этапе компиляции мы не знаем, какие инструкции на самом деле нужны, а какие нет, а потому приходится загружать все.
Это не так страшно, если приложение не очень большое, а ресурсов компьютера достаточно. Но когда речь заходит о мобильных устройствах, размер бандла и требуемые объемы памяти становятся критически важными параметрами.
Давайте предложим компонентам обращаться не к движку рендеринга, а непосредственно к инструкциям. Пусть каждый компонент заранее определится, какие ему нужны, тогда на этапе компиляции приложения можно будет отсеять невостребованные.
Таким образом решается задача tree-shakable — уменьшения размера сборки, которая загружается в браузер за счет удаления неиспользуемых фрагментов кода.
Поговорим о потреблении памяти
Давайте еще раз посмотрим, как Virtual DOM осуществляет рендеринг. На первом шаге мы на базе реального DOM строим виртуальное дерево, следовательно — для каждого элемента нам нужно выделить какой-то объем памяти. Когда приходит время отрисовать новое состояние, мы строим новое виртуальное дерево, а значит — для каждого элемента повторно выделяется нужный объем памяти. Далее старое и новое виртуальные деревья сравниваются, и если есть разница — изменения применяются к физическому DOM.
Получается, что на каждом этапе рендера нам требуется повторно выделять память в размере, необходимом для того, чтобы отрисовать дерево целиком.
И снова — повод задуматься. Если у нас на какой-то момент времени есть существующее актуальное состояние DOM уже отрисованное браузером, и изменяется только один элемент — зачем нам строить новое виртуальное дерево целиком? Что, если можно было бы пробежаться по старому и взять из него для построения нового те элементы, которые не изменились? Тогда нам потребуется дополнительно выделять память только в случае создания нового узла или изменения старого.
Этот алгоритм реализует небольшая (всего 2,6 КБ) библиотека под названием Incremental DOM.
Здравый вопрос — почему же тогда этот вариант до сих пор используется не всеми? Из «большой тройки» фреймворков, Incremental DOM применяется только в Angular, а React и Vue предпочитают старый добрый Virtual DOM.
Все дело в том, что Angular — единственный фреймворк большой тройки, изначально построенный на архитектуре с использованием template, когда все компоненты пишутся с использованием шаблонов. Посмотрим на пример функции renderPart() из документации библиотеки Incremental DOM
и мысленно подставим на место elementOpen() и elementClose() — selector компонента Angular, а на место text() — template.
Для тех, кто добрался до финала
Пришло время подводить итоги и разобраться с многоточием, которое было поставлено во вступлении.
Не пытайся изменить DOM… без причины. Первое, что нужно понять: DOM существует. Но его задача — обеспечить отрисовку страницы, заранее подготовленной с помощью других инструментов. Таких, как Shadow, Virtual и Incremental DOM. И современный разработчик должен знать, в чем они схожи, чем отличаются и как выбрать наиболее подходящее решение для конкретной задачи.
Если есть чем дополнить — комментарии можно оставлять под текстом или мне в телеграм.