Shadow DOM
Теневой DOM («Shadow DOM») используется для инкапсуляции. Благодаря ему в компоненте есть собственное «теневое» DOM-дерево, к которому нельзя просто так обратиться из главного документа, у него могут быть изолированные CSS-правила и т.д.
Встроенный теневой DOM
Задумывались ли вы о том, как устроены и стилизованы сложные браузерные элементы управления?
Браузер рисует их своими силами и по своему усмотрению. Их DOM-структура обычно нам не видна, но в инструментах разработчика можно её посмотреть. К примеру, в Chrome для этого нужно активировать пункт «Show user agent shadow DOM».
После этого выглядит так:

То, что находится под #shadow-root – и называется «shadow DOM» (теневой DOM).
Мы не можем получить доступ к теневому DOM встроенных элементов с помощью обычных JavaScript-вызовов или с помощью селекторов. Это не просто обычные потомки, это мощное средство инкапсуляции.
В примере выше можно увидеть полезный атрибут pseudo . Он нестандартный и существует по историческим причинам. С его помощью можно стилизовать подэлементы через CSS, например, так:
/* делаем цвет шкалы ползунка красным */ input::-webkit-slider-runnable-track
Ещё раз заметим, что pseudo – нестандартный атрибут. Если говорить хронологически, то сначала браузеры начали экспериментировать с инкапсуляцией внутренних DOM-структур для элементов, а уже потом, через некоторое время, появился стандарт Shadow DOM, который позволяет делать то же самое нам, разработчикам.
Далее мы воспользуемся современным стандартом Shadow DOM, описанным в спецификации DOM spec и других спецификациях.
Теневое дерево
Каждый DOM-элемент может иметь 2 типа поддеревьев DOM:
- Light tree – обычное, «светлое», DOM-поддерево, состоящее из HTML-потомков. Все поддеревья, о которых мы говорили в предыдущих главах, были «light».
- Shadow tree – скрытое, «теневое», DOM-поддерево, не отражённое в HTML, скрытое от посторонних глаз.
Если у элемента имеются оба поддерева, браузер отрисовывает только теневое дерево. Также мы всё же можем задать «композицию» теневого и обычного деревьев. Позже в главе Слоты теневого DOM, композиция мы рассмотрим детали.
Теневое дерево можно использовать в пользовательских элементах (Custom Elements), чтобы спрятать внутренности компонента и применить к ним локальные стили.
Например, этот элемент прячет свой внутренний DOM в теневом дереве:
А вот как получившийся DOM выглядит в инструментах разработчика в Chrome, весь контент внутри «#shadow-root»:

Итак, вызов elem.attachShadow() создаёт теневое дерево.
Есть два ограничения:
- Для каждого элемента мы можем создать только один shadow root.
- В качестве elem может быть использован пользовательский элемент (Custom Element), либо один из следующих элементов: «article», «aside», «blockquote», «body», «div», «footer», «h1…h6», «header», «main», «nav», «p», «section» или «span». Остальные, например,
, не могут содержать теневое дерево.
Свойство mode задаёт уровень инкапсуляции. У него может быть только два значения:
- «open» – корень теневого дерева («shadow root») доступен как elem.shadowRoot . Любой код может получить теневое дерево elem .
- «closed» – elem.shadowRoot всегда возвращает null . До теневого DOM в таком случае мы сможем добраться только по ссылке, которую возвращает attachShadow (и, скорее всего, она будет спрятана внутри класса). Встроенные браузерные теневые деревья, такие как у , закрыты. До них не добраться.
С возвращаемым методом attachShadow объектом корнем теневого дерева, можно работать как с обычным DOM-элементом: менять его innerHTML или использовать методы DOM, такие как append , чтобы заполнить его.
Элемент с корнем теневого дерева называется – «хозяин» (host) теневого дерева, и он доступен в качестве свойства host у shadow root:
// при условии, что , иначе elem.shadowRoot равен null alert(elem.shadowRoot.host === elem); // true
Инкапсуляция
Теневой DOM отделён от главного документа:
- Элементы теневого DOM не видны из обычного DOM через querySelector . В частности, элементы теневого DOM могут иметь такие же идентификаторы, как у элементов в обычном DOM (light DOM). Они должны быть уникальными только внутри теневого дерева.
- У теневого DOM свои стили. Стили из внешнего DOM не применятся.
/* стили документа не применятся в теневом дереве внутри #elem (1) */ p ); // у теневого дерева свои стили (2) elem.shadowRoot.innerHTML = ` p Hello, John!
`; // виден только запросам внутри теневого дерева (3) alert(document.querySelectorAll('p').length); // 0 alert(elem.shadowRoot.querySelectorAll('p').length); // 1
- Стили главного документа не влияют на теневое дерево.
- …Но свои внутренние стили работают.
- Чтобы добраться до элементов в теневом дереве, нам нужно искать их изнутри самого дерева.
Ссылки
- DOM: https://dom.spec.whatwg.org/#shadow-trees
- Совместимость: https://caniuse.com/#feat=shadowdomv1
- Теневой DOM упоминается во многих других спецификациях, например DOM Parsing указывает, что у shadow root есть innerHTML .
Итого
Теневой DOM – это способ создать свой, изолированный, DOM для компонента.
- shadowRoot = elem.attachShadow() – создаёт теневой DOM для elem . Если mode=»open» , он доступен через свойство elem.shadowRoot .
- Мы можем создать подэлементы внутри shadowRoot с помощью innerHTML или других методов DOM.
Элементы теневого DOM:
- Обладают собственной областью видимости идентификаторов
- Невидимы JavaScript селекторам из главного документа, таким как querySelector ,
- Стилизуются своими стилями из теневого дерева, не из главного документа.
Теневой DOM, если имеется, отрисовывается браузером вместо обычных потомков (light DOM). В главе Слоты теневого DOM, композиция мы разберём, как делать их композицию.
Shadow dom что это

- Главная /
- JAVASCRIPT /
- Shadow dom javascript
Shadow dom javascript

Доброго времени суток! В данной небольшой статье я вам расcкажу про Shadow dom(теневой dom) в javascript. Итак, поехали!
Как вы знаете работа с html элементами в javascript осуществляются через работу с DOM. DOM представляет собой архитектуру элементов(тегов) построенную в браузере из html кода. Для просмотра данной архитектуры достаточно зайти в любой инспектор элементов. К примеру в google chrome для того чтобы вызвать инспектор необходимо нажать горячие клавиши ctrl + shift + i и в результате вы увидите dom дерево веб страницы. Данный DOM является открытым и может наследовать все стили, скрипты подключенные к данной странице.
В современных браузерах помимо открытого dom дерева поддерживается еще так называемый теневой dom(скрытое dom дерево). Shadom dom представляет собой изолированную архитектуру элементов от основного dom(открытого dom дерева). Некоторые сложные элементы веб страницы такие как к примеру video, audio, input[range] и т. п. состоят из отдельных компонентов построенных с помощью shadow dom.
Рассмотрим на примере тега video.
Если мы зайдем в инспектор кода и посмотрим на данный тег то увидим точно такую же структуру описанную нами в html коде. Никаких дополнительных компонентов мы не увидим, потому что изначально теневое дерево скрыто. Чтобы его просмотреть переходим в инспектор(на примере браузера google chrome), в правом верхнем меню отладочной панели рядом с крестиком кликаем по значку с тремя точками и выбираем пункт настройки(settings). Далее в колонке Elements активируем чекбокс ‘Show user agent shadow DOM’. После возвращаемся в инспектор и смотрим на тег video. Как видите он теперь не пустой. Если мы его раскроем то увидим там такую строчку ‘#shadow-root (user-agent)’ которая содержит множество различных элементов. Поздравляю! Вы попали в скрытое dom дерево веб страницы, а точнее тега video. Теперь вы знаете что видеоплеер html5 состоит из множества разных компонентов(тегов). Следует учесть что dom тега видео формируется браузером и мы не можем иметь доступ к его скрытым элементам для манипулирования. Однако ничто не мешает нам создавать у элементов свои скрытые компоненты, чем мы далее и займемся.
Итак введение закончилось, переходим к коду.
p Заголовок текста
Здесь создана простая верстка к которой применены стили и скрипты. Элемент div с классом shadow будет root элементом нашего скрытого dom дерева(shadow). Причем желательно чтобы root элемент не содержал в себе ничего, так как при создании в нем shadow dom все его содержимое перестанет отображаться. Это следует запомнить!
var div = document.querySelector('.shadow'); var shadow = div.attachShadow();
Этим кодом мы создаем внутри элемента ‘.shadow’ теневой dom(shadow dom). Если зайдем в инспектор кода и раскроем данный элемент то увидим #shadow-root (open).mode
— означает что мы может обращаться к теневому dom дереву через элемент с помощью свойства shadowRoot пример:
console.log(div.shadowRoot);
Здесь будет в консоль возвращена ссылка на dom дерево элемента ‘.shadow’ по сути это то что возвращено в переменную ‘shadow’.
Если поставить то свойство ‘shadowRoot’ будет возвращать значение null.
Теневое dom дерево мы создали, теперь можно его наполнять данными(html кодом, стилями, скриптами).
var div = document.querySelector('.shadow'); var p = document.createElement('p'); p.innerText = 'Середина текста'; var shadow = div.attachShadow(); shadow.appendChild(p);
В данном примере мы создали тег ‘p’ с текстом ‘Середина текста’ и добавили в скрытое dom дерево. Обратите внимание что работа с shadow dom ничем не отличается от работы с обычным dom деревом. Однако как вы уже могли заметить внешние стили и скрипты к тегу ‘p’ в shadow dom не применились. Это говорит о том что контекст теневого дерева полностью изолирован от обычного DOM.
var div = document.querySelector('.shadow'); var p = document.createElement('p'); p.innerText = 'Середина текста'; var shadow = div.attachShadow(); shadow.innerHTML = ` p `; var script = document.createElement('script'); script.innerHTML = ` shadow.querySelector('p').addEventListener('click', function()< this.style.fontSize = '20px'; >); `; shadow.appendChild(p); shadow.appendChild(script);
Здесь в shadow dom были добавлены стили и скрипт к тегу ‘p’. Обратите внимание что в самом скрипте мы обращаемся к тегу ‘p’ не через document(document.querySelector(‘p’)), а переменную shadow(shadow.querySelector(‘p’)) которая является ссылкой на корневой элемент shadow dom.
var shadow = div.attachShadow();
Так сделано потому что элементы shadow dom не содержаться в обычном dom и обращаться к ним по селектору мы может только через корневой элемент.
Если кратко подвести итог, то получается что shadow dom позволяет нам создавать полностью независимые от основного контекста и друг от друга компоненты которые могут включать в себя собственную структуру(html элементы), стили(css) и логику(javascript).
Для взаимодействия компонентов теневого dom между собой или основным контекстом мы можем использовать ссылку на корневой элемент. Пример:
var div = document.querySelector('.shadow'); var shadow = div.attachShadow();
Переменная shadow является корневым элементом shadow dom у селектора ‘.shadow’. К примеру если мы хотим поменять цвет у элемента ‘p’ в теневом дереве.
var div = document.querySelector('.shadow'); var p = document.createElement('p'); p.innerText = 'Середина текста'; var shadow = div.attachShadow(); shadow.appendChild(p); shadow.querySelector('p').style.color = 'orange';
Установили цвет у элемента ‘p’ — orange(оранжевый) через обращение к корневому элементу(root).
Общую суть я надеюсь вы поняли. На этом у меня все. Не забывайте подписываться на группу Вконтакте и переходите на канал Youtube.
Всем спасибо за внимание! Пока.
Оцените статью:
Статьи
- Методы идентификации подстрок в ES6
- CSS перенос слов
- Grid CSS
- Vibrate api
- Диалоговые окна tkinter
- память выделяемая php
- meter html
- selection javascript
- Как установить go
Комментарии
Внимание. Все комментарии проходят модерацию перед публикацией!
В данном разделе пока нет комментариев!
© 2017-2024 webfanat.com Все права защищены.

Запись экрана
Данное расширение позволяет записывать экран и выводит видео в формате webm
Добавить приложение на рабочий стол
Виртуальный DOM и детали его реализации в React
Виртуальный DOM (VDOM) — это концепция программирования, в которой идеальное или «виртуальное» представление пользовательского интерфейса хранится в памяти и синхронизируется с «настоящим» DOM при помощи библиотеки, такой как ReactDOM. Этот процесс называется согласованием.
Такой подход и делает API React декларативным: вы указываете, в каком состоянии должен находиться пользовательский интерфейс, а React добивается, чтобы DOM соответствовал этому состоянию. Это абстрагирует манипуляции с атрибутами, обработку событий и ручное обновление DOM, которые в противном случае пришлось бы использовать при разработке приложения.
Поскольку «виртуальный DOM» — это скорее паттерн, чем конкретная технология, этим термином иногда обозначают разные понятия. В мире React «виртуальный DOM» обычно ассоциируется с React-элементами , поскольку они являются объектами, представляющими пользовательский интерфейс. Тем не менее, React также использует внутренние объекты, называемые «волокнами» (fibers), чтобы хранить дополнительную информацию о дереве компонентов. Их также можно считать частью реализации «виртуального DOM» в React.
Теневой DOM похож на виртуальный DOM?
Нет, они совсем разные. Теневой DOM (Shadow DOM) — это браузерная технология, предназначенная в основном для определения области видимости переменных и CSS в веб-компонентах. Виртуальный DOM — это концепция, реализованная библиотеками в JavaScript поверх API браузера.
Что такое «React Fiber»?
Fiber — новый механизм согласования в React 16, основная цель которого сделать рендеринг виртуального DOM инкрементным. Узнать больше об этом.
Что такое DOM, Shadow DOM, Virtual DOM?
Объектная модель документа, или «DOM», является программным интерфейсом доступа к элементам веб-страниц. По сути, это API страницы, позволяющий читать и манипулировать содержимым, структурой и стилями страницы. Давайте разберемся как это устроено и как это работает.
— Как строится веб-страница?
Процесс преобразования исходного HTML-документа в отображаемую стилизованную и интерактивную страницу, называется «критическим путем рендеринга» (Critical Rendering Path). Хотя этот процесс можно разбить на несколько этапов, эти этапы можно условно сгруппировать в два этапа. В первом браузер анализирует документ, чтобы определить, что в конечном итоге будет отображаться на странице, а во второй браузер выполняет рендеринг.

Результатом первого этапа является то, что называется «render tree» (дерево рендеринга). Дерево рендеринга — это представление элементов HTML, которые будут отображаться на странице, и связанных с ними стилей. Чтобы построить это дерево, браузеру нужны две вещи:
- CSSOM, представление стилей, связанных с элементами
- DOM, представление элементов
— Из чего состоит DOM?
DOM — это объектное представление исходного HTML-документа. Он имеет некоторые различия, как мы увидим ниже, но по сути это попытка преобразовать структуру и содержание документа HTML в объектную модель, которая может использоваться различными программами.
Структура объектов DOM представлена тем, что называется «деревом узлов». Оно так называется, потому что его можно рассматривать как дерево с одним родительским элементом, который разветвляется на несколько дочерних ветвей, каждая из которых может иметь листья. В этом случае родительский «элемент» — это корневой элемент, дочерние «ветви» — это вложенные элементы, а «листья» — это содержимое внутри элементов.
Давайте рассмотрим этот HTML-документ в качестве примера:
My first web page Hello, world!
How are you?
Этот документ может быть представлен в виде следующего дерева узлов:

— Чем DOM не является
В приведенном выше примере кажется, что DOM является отображением 1:1 исходного HTML-документа. Однако, как я уже говорил, есть различия. Чтобы полностью понять, что такое DOM, нам нужно взглянуть на то, чем он не является.
— DOM не является копией исходного HTML
Хотя DOM создан из HTML-документа, он не всегда точно такой же. Есть два случая, в которых DOM может отличаться от исходного HTML.
— 1. Когда HTML содержит ошибки разметки
DOM — это интерфейс доступа к действительным (то есть уже отображаемым) элементам документа HTML. В процессе создания DOM, браузер сам может исправить некоторые ошибки в коде HTML.
Рассмотрим в качестве примера этот HTML-документ:
Hello, world!
В документе отсутствуют элементы и , что является обязательным требованием для HTML. Но если мы посмотрим на получившееся дерево DOM, то увидим, что это было исправлено:

— 2. Когда DOM модифицируется кодом JavaScript
Помимо того, что DOM является интерфейсом для просмотра содержимого документа HTML, он сам также может быть изменен.
Мы можем, например, создать дополнительные узлы для DOM, используя Javascript.
var newParagraph = document.createElement("p"); var paragraphContent = document.createTextNode("I'm new!"); newParagraph.appendChild(paragraphContent); document.body.appendChild(newParagraph);
Этот код изменит DOM, но изменения не отобразятся в документе HTML.
— DOM — это не то, что вы видите в браузере (то есть, дерево рендеринга)
В окне просмотра браузера вы видите дерево рендеринга, которое, как я уже говорил, является комбинацией DOM и CSSOM. Чем отличается DOM от дерева рендеринга, так это то, что последнее состоит только из того, что в конечном итоге будет отображено на экране.
Поскольку дерево рендеринга имеет отношение только к тому, что отображается, оно исключает элементы, которые визуально скрыты. Например, элементы, у которых есть стили с display: none.
Hello, world!
DOM будет включать элемент
:

Однако дерево рендеринга и, следовательно, то, что видно в окне просмотра, не будет включено в этот элемент.

— DOM — это не то, что отображается в DevTools
Это различие немного меньше, потому что инспектор элементов DevTools обеспечивает самое близкое приближение к DOM, которое мы имеем в браузере. Однако инспектор DevTools содержит дополнительную информацию, которой нет в DOM.
Лучший пример этого — псевдоэлементы CSS. Псевдоэлементы, созданные с использованием селекторов ::before и ::after, являются частью CSSOM и дерева рендеринга, но технически не являются частью DOM. Это связано с тем, что DOM создается только из исходного HTML-документа, не включая стили, примененные к элементу.
Несмотря на то, что псевдоэлементы не являются частью DOM, они есть в нашем инспекторе элементов devtools.

— Резюме
DOM — это интерфейс к HTML-документу. Он используется браузерами как первый шаг к определению того, что визуализировать в окне просмотра, и кодом JavaScript для изменения содержимого, структуры или стиля страницы.
Хотя DOM похож на другие формы исходного документа HTML, он отличается по ряду причин:
- Это всегда верный (валидный) HTML код
- Это модель, которая может быть изменена с помощью JavaScript
- В него не входят псевдоэлементы (такие как ::after)
- В него входят скрытые элементы (такие как display: none)
Что такое Shadow DOM?
Все элементы и стили в HTML-документе и, следовательно, в DOM, находятся в одной большой глобальной области. Любой элемент на странице может быть доступен с помощью метода document.querySelector(), независимо от того, насколько глубоко он вложен в документ или где он находится. Точно так же CSS, примененный к документу, может выбрать любой элемент, независимо от того, где он находится.
Такое поведение может быть очень удобным, если мы хотим применить стили ко всему документу. Невероятно полезно иметь возможность выбирать каждый отдельный элемент на странице и устанавливать, например, их размеры одной строкой.
С другой стороны, бывают случаи, когда элемент требует полной изоляции, и мы не хотим, чтобы на него влияли даже глобальные стили. Хорошим примером этого являются сторонние виджеты, такие как кнопка «Follow» для Twitter на некоторых страницах. Такая кнопка может отображаться как обычная ссылка.
Но если вы изучите этот элемент в DevTools, вы заметите, что кнопка является элементом , который загружает небольшой документ со стилизованной кнопкой, которую вы на самом деле видите.

Это единственный способ, которым Twitter может гарантировать, что предполагаемый стиль их виджета останется незатронутым любым CSS в документе. Хотя существуют способы использования каскада стилей для достижения того же результата, но ни один другой метод не даст такой же гарантии, как , хотя и он не идеален.
Shadow DOM был создан для обеспечения возможности изоляции и компонентизации непосредственно на веб-платформе без необходимости полагаться на такие инструменты, как .
— DOM внутри DOM
Вы можете думать о теневом DOM как о «DOM внутри DOM». Это собственное изолированное дерево DOM со своими элементами и стилями, полностью изолированное от исходного DOM.
Хотя только недавно его стали использовать программисты, теневой DOM годами использовался пользовательскими агентами для создания и оформления сложных компонентов, таких как элементы формы. Для примера возьмем элемент ввода диапазона. Чтобы создать его на странице, все, что нам нужно сделать, это добавить следующий элемент:
Если мы посмотрим глубже, мы увидим, что этот один элемент фактически состоит из нескольких меньших элементов , управляющих дорожкой и ползунком.

Это делается с помощью теневого DOM. Элемент, который предоставляется HTML-документу как простой , но в реальности он состоит из нескольких элементов и стилей, связанных с компонентом, которые не являются частью глобальной области видимости DOM.
— Как работает shadow DOM
Чтобы проиллюстрировать, как работает теневой DOM, давайте воссоздаем кнопку Twitter «Follow», используя теневой DOM вместо .
Сначала мы начнем с shadow host (теневого хоста). Это обычный элемент HTML в исходном DOM, к которому мы хотим присоединить новый теневой DOM. Для такого компонента, как кнопка «Follow», он также может содержать запасной элемент, который мы хотели бы отобразить, если JavaScript не был включен на странице или теневой DOM не поддерживается.
Чтобы прикрепить теневой DOM к нашему хосту, мы используем метод attachShadow().
const shadowEl = document.querySelector(".shadow-host"); const shadow = shadowEl.attachShadow();
Это создаст пустой shadow root (теневой корень) как дочерний элемент нашего теневого хоста. Теневой корень — это начало нового теневого DOM, так же, как элемент является началом исходного DOM. Мы можем увидеть наш теневой корень в инспекторе devtools с помощью #shadow-root.

Хотя теперь дочерние элементы HTML видны в инспекторе, они больше не видны на странице, так как заработал теневой корень.
Далее мы хотим создать контент для формирования нашего нового теневого дерева. Чтобы создать нашу кнопку «Follow», нам нужен только новый элемент , который будет почти таким же, как и у уже имеющейся резервной ссылки, но со значком.
const link = document.createElement("a"); link.href = shadowEl.querySelector("a").href; link.innerHTML = ` $ `;
Далее мы добавим этот новый элемент в нашу теневую DOM так же, как добавляем любой элемент в качестве дочернего элемента к другому с помощью метода appendChild().
shadow.appendChild(link);
Наконец, мы можем добавить несколько стилей, создав элемент и добавив его к теневому корню.
const styles = document.createElement("style"); styles.textContent = ` a, span < vertical-align: top; display: inline-block; box-sizing: border-box; >a < height: 20px; padding: 1px 8px 1px 6px; background-color: #1b95e0; color: #fff; border-radius: 3px; font-weight: 500; font-size: 11px; font-family:'Helvetica Neue', Arial, sans-serif; line-height: 18px; text-decoration: none; >a:hover < background-color: #0c7abf; >span < position: relative; top: 2px; width: 14px; height: 14px; margin-right: 3px; background: transparent 0 0 no-repeat; background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E); >`; shadow.appendChild(styles);
— DOM против shadow DOM
В некотором смысле, теневой DOM является «облегченной» версией DOM. Как и DOM, он является представлением элементов HTML, которое используется для определения того, что следует отображать на странице, и позволяет изменять элементы. Но в отличие от DOM, теневой DOM не использует глобальные стили документа. Теневой DOM, как следует из названия, всегда присоединен к элементу в обычном DOM. Без базовых элементов DOM, теневой DOM не существует.
Что такое Virtual DOM?
Напомним, что Document Object Model (Объектная Модель Документа) — это объектное представление HTML документа и интерфейс для управления этим объектом. Shadow DOM можно рассматривать как «облегченную» версию DOM. Это также объектно-ориентированное представление элементов HTML, но Shadow DOM позволяет нам разделить основной DOM на меньшие изолированные части, которые можно использовать в документах HTML.
Другой похожий термин, это «Virtual DOM». Хотя эта концепция существует уже несколько лет, она стала более популярной благодаря использованию ее в различных фреймворках, таких как React, Vuejs и т.д..
— Зачем нам нужен Virtual DOM?
Чтобы понять, почему возникла концепция виртуального DOM, давайте вернемся к DOM. Как я уже упоминал, в DOM есть две части — объектное представление документа HTML и API для управления этим объектом.
Например, давайте возьмем в качестве примера простой HTML-документ с неупорядоченным списком и одним элементом списка.
Этот документ может быть представлен как следующее DOM дерево:
html head lang="en" body ul li "List item"
Допустим, мы хотим изменить содержимое первого элемента списка на «List item one», а также добавить второй элемент списка. Для этого нам нужно будет использовать API DOM, чтобы найти элементы, которые мы хотим обновить, создать новые элементы, добавить атрибуты и контент, а затем, наконец, обновить сами элементы DOM.
const listItemOne = document.getElementsByClassName("list__item")[0]; listItemOne.textContent = "List item one"; const list = document.getElementsByClassName("list")[0]; const listItemTwo = document.createElement("li"); listItemTwo.classList.add("list__item"); listItemTwo.textContent = "List item two"; list.appendChild(listItemTwo);
— DOM не был сделан для этого .
Когда в 1998 году была выпущена первая спецификация для DOM, мы создавали и управляли веб-страницами по-другому. API DOM использовался для создания и обновления содержимого страниц гораздо реже, чем это делается сегодня.
Простые методы, такие как document.getElementsByClassName() подходят для небольшого количества изменений, но если мы обновляем несколько элементов на странице каждые несколько секунд, это может стать очень дорогостоящим, чтобы постоянно запрашивать и обновлять DOM.
Более того, из-за способа настройки API-интерфейсов обычно проще выполнять более дорогостоящие операции, когда мы обновляем большие части документа, чем находить и обновлять конкретные элементы. Возвращаясь к нашему примеру со списком, в некотором смысле проще заменить весь неупорядоченный список новым, чем модифицировать определенные элементы.
В этом конкретном примере разница в производительности между методами, вероятно, незначительна. Однако по мере роста размера веб-страницы становится все более важным выбирать и обновлять только то, что необходимо.
— . но причем здесь виртуальный DOM!
Виртуальный DOM был создан для решения этих проблем, связанных с необходимостью частого обновления DOM более производительным способом. В отличие от DOM или Shadow DOM, Virtual DOM не является официальной спецификацией, а представляет собой новый метод взаимодействия с DOM.
Виртуальный DOM может рассматриваться как копия исходного DOM. Этой копией можно часто манипулировать и обновлять, не используя API DOM. После того как все обновления были внесены в виртуальный DOM, мы можем посмотреть, какие конкретные изменения необходимо внести в исходный DOM, и сделать их целевым и оптимизированным способом.
— Как выглядит виртуальный DOM?
Слово виртуальный имеет тенденцию добавлять определеную загадочность там где ее на самом деле нет. Фактически, виртуальный DOM — это просто обычный объект JavaScript.
Давайте вернемся к дереву DOM, которое мы создали ранее:
html head lang="en" body ul li "List item"
Дерево может быть представлено как объект JavaScript:
const vdom = < tagName: "html", children: [ < tagName: "head" >, < tagName: "body", children: [ < tagName: "ul", attributes: < "class": "list" >, children: [ < tagName: "li", attributes: < "class": "list__item" >, textContent: "List item" > // end li ] > // end ul ] > // end body ] > // end html
Мы можем думать об этом объекте как о нашем виртуальном DOM. Как и исходный DOM, это объектное представление нашего HTML-документа. Но так как это простой объект JavaScript, мы можем свободно и часто манипулировать им, не касаясь реального DOM, пока нам это не понадобится.
Вместо того, чтобы использовать один объект для всего объекта, более распространенной является работа с небольшими разделами виртуального DOM. Например, мы можем работать с компонентом списка, который будет привязан к нашему неупорядоченному элементу списка.
const list = < tagName: "ul", attributes: < "class": "list" >, children: [ < tagName: "li", attributes: < "class": "list__item" >, textContent: "List item" > ] >;
— Как работает виртуальный DOM
Теперь, когда мы увидели, как выглядит виртуальный DOM, как он работает для решения проблем производительности и удобства использования DOM?
Как я уже упоминал, мы можем использовать виртуальный DOM, чтобы выделить конкретные изменения, которые необходимо внести в DOM, и сделать эти конкретные обновления по отдельности. Давайте вернемся к нашему неупорядоченному списку и внесем те же изменения, что и в DOM API.
Первое, что мы сделаем, это сделаем копию виртуального DOM, содержащего изменения, которые мы хотим сделать. Поскольку нам не нужно использовать API DOM, мы фактически можем просто создать новый объект полностью.
const copy = < tagName: "ul", attributes: < "class": "list" >, children: [ < tagName: "li", attributes: < "class": "list__item" >, textContent: "List item one" >, < tagName: "li", attributes: < "class": "list__item" >, textContent: "List item two" > ] >;
Эта копия используется для создания того, что называется «diff» между исходным и виртуальным DOM, в данном случае исходным списком, и обновленным списком. Diff может выглядеть примерно так:
const diffs = [ < newNode: < /* new version of list item one */ >, oldNode: < /* original version of list item one */ >, index: /* index of element in parent's list of child nodes */ >, < newNode: < /* list item two */ >, index: < /* */ >> ];
В этом разделе приведены инструкции по обновлению фактического DOM. Как только все различия собраны, мы можем пакетно вносить изменения в DOM, делая только необходимые обновления.
Например, мы могли бы перебрать каждый diff и добавить нового потомка или обновить старого в зависимости от того, что указано в diff.
const domElement = document.getElementsByClassName("list")[0]; 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); >>);
Обратите внимание, что это действительно упрощенная версия того, как реально работает виртуальный DOM, и есть много случаев, которые я здесь не описал.
— Виртуальный DOM и фреймворки
В реальности с виртуальным DOM чаще работают через фреймворки, а не взаимодействуют с ним напрямую, как я показал в примере выше.
Фреймворки, такие как React и Vue, используют концепцию виртуального DOM для более производительных обновлений DOM. Например, наш компонент списка может быть написан в React следующим образом.
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);
Если бы мы хотели обновить наш список, мы могли бы просто переписать весь шаблон списка и снова вызвать ReactDOM.render(), передав новый список.
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);
Поскольку React использует виртуальный DOM, даже если мы перерисовываем весь шаблон, обновляются только те части, которые действительно изменяются. Если мы посмотрим на наши инструменты разработчика, когда произойдут изменения, мы увидим конкретные элементы и конкретные части элементов, которые меняются.
— DOM против Virtual DOM
Напомним, что виртуальный DOM — это инструмент, который позволяет нам взаимодействовать с элементами DOM более простым и производительным способом. Это JavaScript-объектное представление DOM, которое мы можем изменять так часто, как нам нужно. Изменения, внесенные в этот объект, затем сопоставляются, а изменения в реальном DOM производятся намного реже.