Веб-компоненты не установлены на край

Обновлено: 21.11.2024

Веб-компоненты – это набор различных технологий, позволяющих создавать повторно используемые настраиваемые элементы, функциональность которых изолирована от остального кода, и использовать их в своих веб-приложениях.

Концепции и использование

Как разработчики, мы все знаем, что повторное использование кода — это хорошая идея. Традиционно это было не так просто для пользовательских структур разметки — подумайте о сложном HTML (и связанном с ним стиле и сценарии), который вам иногда приходилось писать для отображения пользовательских элементов управления пользовательского интерфейса, и о том, как их многократное использование может превратить вашу страницу в беспорядок. если вы не будете осторожны.

Веб-компоненты предназначены для решения таких проблем — они состоят из трех основных технологий, которые можно использовать вместе для создания универсальных пользовательских элементов с инкапсулированной функциональностью, которые можно повторно использовать где угодно, не опасаясь коллизий кода.

  • Пользовательские элементы. Набор API-интерфейсов JavaScript, которые позволяют определять настраиваемые элементы и их поведение, которые затем можно использовать по желанию в пользовательском интерфейсе.
  • Shadow DOM: набор API-интерфейсов JavaScript для присоединения инкапсулированного "теневого" дерева DOM к элементу, который отображается отдельно от основного документа DOM, и управления соответствующей функциональностью. Таким образом, вы можете сохранить функции элемента в тайне, чтобы их можно было записывать в сценарии и стилизовать, не опасаясь столкновения с другими частями документа.
  • Шаблоны HTML. Элементы и позволяют создавать шаблоны разметки, которые не отображаются на отображаемой странице. Затем их можно многократно использовать в качестве основы структуры пользовательского элемента.

Основной подход к реализации веб-компонента обычно выглядит примерно так:

  1. Создайте класс, в котором вы указываете функциональность веб-компонента, используя синтаксис класса ECMAScript 2015 (дополнительную информацию см. в разделе Классы).
  2. Зарегистрируйте новый настраиваемый элемент с помощью метода CustomElementRegistry.define(), передав ему имя определяемого элемента, класс или функцию, в которой указаны его функциональные возможности, и, при необходимости, элемент, от которого он наследуется.
  3. При необходимости присоедините теневой DOM к пользовательскому элементу с помощью метода Element.attachShadow(). Добавьте дочерние элементы, прослушиватели событий и т. д. в теневой DOM с помощью обычных методов DOM.
  4. При необходимости определите шаблон HTML с помощью и . Снова используйте обычные методы DOM, чтобы клонировать шаблон и прикрепить его к теневой модели DOM.
  5. Используйте свой пользовательский элемент в любом месте страницы, как и любой обычный элемент HTML.

Учебники

Руководство, показывающее, как использовать функции пользовательских элементов для создания простых веб-компонентов, а также изучение обратных вызовов жизненного цикла и некоторых других более сложных функций.

Руководство, в котором рассматриваются основы теневого DOM, показывающие, как прикрепить теневой DOM к элементу, добавить его в дерево теневого DOM, стилизовать его и т. д.

Руководство, показывающее, как определить многократно используемую структуру HTML с помощью элементов и , а затем использовать эту структуру в веб-компонентах.

Ссылка

Пользовательские элементы

Содержит функции, связанные с пользовательскими элементами, в первую очередь метод CustomElementRegistry.define(), используемый для регистрации новых пользовательских элементов, чтобы их можно было затем использовать в документе.

Возвращает ссылку на объект CustomElementRegistry.

Специальные функции обратного вызова, определенные внутри определения класса пользовательского элемента, которые влияют на его поведение:

  • connectedCallback : вызывается, когда настраиваемый элемент впервые подключается к модели DOM документа.
  • disconnectedCallback : вызывается, когда настраиваемый элемент отключается от DOM документа.
  • adoptedCallback: вызывается при перемещении пользовательского элемента в новый документ.
  • attributeChangedCallback: вызывается при добавлении, удалении или изменении одного из атрибутов пользовательского элемента.

Определены следующие расширения:

  • Глобальный HTML-атрибут is: позволяет указать, что стандартный HTML-элемент должен вести себя как зарегистрированный настраиваемый встроенный элемент.
  • Опция "is" метода Document.createElement(): позволяет создать экземпляр стандартного HTML-элемента, который ведет себя как заданный зарегистрированный настраиваемый встроенный элемент.

Псевдоклассы, относящиеся конкретно к пользовательским элементам:

    : Соответствует любому определенному элементу, включая встроенные элементы и пользовательские элементы, определенные с помощью CustomElementRegistry.define() ). : выбирает теневой хост теневого DOM, содержащего CSS, внутри которого он используется. : выбирает теневой хост теневого DOM, содержащего CSS, внутри которого он используется (так что вы можете выбрать пользовательский элемент внутри его теневого DOM), но только если селектор, указанный в качестве параметра функции, соответствует теневому хосту.: выбирает теневой хост теневого DOM, содержащего CSS, внутри которого он используется (так что вы можете выбрать пользовательский элемент внутри его теневого DOM) — но только если селектор, указанный в качестве параметра функции, соответствует предку(ам) теневого хоста в место, где он находится внутри иерархии DOM.

Псевдоэлементы, относящиеся конкретно к пользовательским элементам:

    : представляет любой элемент в теневом дереве, который имеет соответствующий атрибут части.

Тень DOM

Представляет корневой узел теневого поддерева DOM.

Расширения интерфейса Element, связанные с теневым DOM:

  • Метод Element.attachShadow() прикрепляет теневое дерево DOM к указанному элементу.
  • Свойство Element.shadowRoot возвращает теневой корень, прикрепленный к указанному элементу, или null, если теневой корень не прикреплен.

Дополнения к интерфейсу Node, относящиеся к теневой модели DOM:

  • Метод Node.getRootNode() возвращает корень объекта контекста, который может включать теневой корень, если он доступен.
  • Свойство Node.isConnected возвращает логическое значение, указывающее, подключен ли узел (напрямую или косвенно) к объекту контекста, например. объект Document в случае обычного DOM или ShadowRoot в случае теневого DOM.

Расширения интерфейса Event, связанные с теневым DOM:

    : возвращает true, если событие будет распространяться через границу теневого DOM в стандартный DOM, в противном случае — false. : возвращает путь к событию (объекты, для которых будут вызываться слушатели). Это не включает узлы в теневых деревьях, если теневой корень был создан с закрытым ShadowRoot.mode.

Шаблоны HTML

Содержит фрагмент HTML, который не отображается при первоначальной загрузке содержащего документа, но может отображаться во время выполнения с помощью JavaScript, который в основном используется в качестве основы для пользовательских структур элементов. Связанный интерфейс DOM — HTMLTemplateElement .

Заполнитель внутри веб-компонента, который можно заполнить собственной разметкой, что позволяет создавать отдельные деревья DOM и представлять их вместе. Связанный интерфейс DOM — HTMLSlotElement .

Глобальный HTML-атрибут слота

Назначает элементу слот в теневом дереве теневого DOM.

Атрибут только для чтения, который возвращает ссылку на элемент, в который вставлен этот элемент.

Атрибут только для чтения, возвращающий ссылку на элемент, в который вставлен этот текстовый узел.

Расширения интерфейса Element, связанные со слотами:

    : возвращает имя теневого слота DOM, прикрепленного к элементу.

Псевдоэлементы, относящиеся конкретно к слотам:

Инициируется для экземпляра HTMLSlotElement (элемент), когда узлы, содержащиеся в этом слоте, изменяются.

Примеры

Мы создаем ряд примеров в нашем репозитории web-components-examples на GitHub. Со временем будет добавлено больше.

Технические характеристики

Элемент шаблона и пользовательские элементы

Теневой DOM

Совместимость с браузером

  • Веб-компоненты по умолчанию поддерживаются в Firefox (версия 63), Chrome, Opera и Edge (версия 79).
  • Safari поддерживает ряд функций веб-компонентов, но меньше, чем указанные выше браузеры.

Чтобы получить подробную информацию о поддержке браузером определенных функций, обратитесь к справочным страницам, перечисленным выше.

Примечание редактора. Это вторая часть серии статей о веб-компонентах, состоящей из двух частей, написанной инженерами Microsoft Edge Трэвисом Лейтхедом и Арроном Эйхольцем. Чтобы узнать больше о нашей точке зрения и опыте работы с веб-компонентами, см. часть первую «Компонентизация в Интернете: обзор веб-компонентов».

Что Microsoft думает о веб-компонентах?

Многие спрашивают нас: "Что Microsoft думает о веб-компонентах?" Веб-компоненты могут многое предложить сообществу. Мы воодушевлены потенциалом этой технологии, позволяющей значительно упростить крупномасштабную веб-разработку и одновременно усилить хорошие декларативные шаблоны, которые уже знакомы в HTML. В нашем участии в стандартах мы также видим отличный способ стимулировать эксперименты сообщества и помочь определить направление будущих стандартов.

Мы довольны текущим обсуждением технических вопросов конкретных веб-компонентов. Производители браузеров и другие участники поднимают и обсуждают правильные вопросы и сложные проблемы. У Уилсона Пейджа из Mozilla есть прекрасный обзор некоторых технических вопросов, которые мы недавно обсуждали на встречах с другими поставщиками. Подмножество этих вопросов (относящихся к пользовательским элементам) запланировано для обсуждения и, надеюсь, решения на предстоящей встрече в конце этого месяца. С тех пор, как были составлены первоначальные спецификации, веб-компоненты прошли долгий путь. Похоже, что в настоящее время происходит межбраузерный консенсус, за которым последует межбраузерное взаимодействие.

Почему Microsoft еще не внедрила веб-компоненты?

Второй вопрос, который мы чаще всего слышим: "Почему вы еще не внедрили веб-компоненты?"

Часто нам нужно пояснить, что веб-компоненты — это не отдельная функция. «Семейство веб-компонентов» может быть более подходящим названием — они включают в себя множество отдельных технологий, работающих вместе — и в этом секрет их потенциала (подробнее об этом во вчерашней публикации «Компонентизация в Интернете: обзор веб-компонентов»). .") В семейство входят такие технологии, как Shadow DOM, настраиваемые элементы, шаблоны и импорт, но настраиваемые свойства CSS также могут считаться частью этого семейства. По мере того, как мы узнаем о масштабах и охвате усилий CSS Houdini, мы можем видеть, как это может сыграть важную роль в разработке стилей компонентов, которые не зависят от конкретного имени тега пользовательского элемента. Есть много вещей, которые нужно рассмотреть, расставить приоритеты и решить, поскольку спецификации еще не завершены, а текущая реализация в Google Chrome уже несовместима с недавними изменениями в спецификациях. Как упоминалось выше, мы продолжаем активно участвовать в процессе стандартизации совместно с разработчиками Google, Apple и Mozilla, а также с несколькими профинансированными библиотеками JavaScript.

Вторая половина ответа связана с тем, где мы концентрируем наши инженерные ресурсы. В рамках разделения движка EdgeHTML (и даже незадолго до этого) мы знали, что пришло время обновить нашу 20-летнюю реализацию DOM. Создание основных функций, таких как Shadow DOM, поверх устаревших внутренних структур данных IE было бы медленным, с ошибками и стоило бы как минимум в два раза больше для реализации. Наши инженерные инвестиции в веб-компоненты действительно начались с наших огромных усилий по обновлению DOM. Обновление DOM не только обеспечит лучшую внутреннюю расширяемость для будущих технологий веб-компонентов, но и обеспечит базовый уровень производительности следующего поколения в DOM Microsoft Edge.

Сейчас мы очень далеко продвинулись в пересмотре нашей архитектуры DOM. Мы ожидаем, что эта работа продолжится после запуска Microsoft Edge в этом месяце. Мы поделимся более подробной информацией об этих усилиях в то время, а также предоставим обновленную информацию о нашей дорожной карте веб-компонентов.

Когда можно ожидать появления веб-компонентов в Microsoft Edge?

По мере того, как мы приближаемся к финишной черте обновленной модели DOM, мы смотрим, что мы можем сделать сейчас, чтобы помочь веб-разработчикам заполнить пробел. Учитывая отзывы наших клиентов и партнеров, мы знаем, что самая сложная часть веб-компонентов для полифилла в неподдерживаемых браузерах — это поведение элемента Template. Элемент Template позволяет эффективно клонировать и заменять содержимое, сохраняя при этом содержимое шаблона вне самого дерева DOM. Мы рады сообщить, что сегодня мы начинаем разработку, чтобы добавить поддержку элементов HTML-шаблонов в Microsoft Edge! Это только начало нашего пути в области веб-компонентов.

После поддержки шаблонов и завершения перезаписи DOM следующей целью является реализация Shadow DOM, второй по сложности функции полифилла, за которой следуют пользовательские элементы. После этого мы планируем оценить остальные спецификации веб-компонентов первого поколения. Естественно, по мере того, как спецификации продолжают развиваться, а важность дополнительных технологий, связанных с веб-компонентами, возрастает, мы можем перетасовывать приоритеты по ходу дела. Как всегда, следите за новостями на нашей странице статуса.

Мы ценим вашу постоянную поддержку и интерес к веб-компонентам. Продолжайте получать отзывы @MSEdgeDev. Спасибо!

– Трэвис Лейтхед, руководитель программы, Microsoft Edge
– Аррон Эйхольц, руководитель программы, Microsoft Edge

Последнюю неделю я посвятил решению своей наименее любимой инженерной задачи: попытке заставить код, работающий в Chrome/Safari/Firefox, работать в Microsoft Edge и Microsoft Internet Explorer. В этом случае я пытался заставить модульные тесты и базовый набор компонентов проекта Elix работать должным образом в Edge и IE 11. Такая работа никогда не доставляла удовольствия. В последнее время мне стало интересно, стоит ли проекту Elix тратить время на поддержку браузеров Microsoft.

Интернет Эксплорер 11

IE 11 по-прежнему поддерживается Microsoft, но по мере того, как основные браузеры уходят от него, работа в IE становится все более анахроничной. Хотя многие современные веб-технологии поставляются с полифиллом или другими средствами для работы с IE 11, набор обходных путей, необходимых сегодня, действительно накопился.

В случае с проектом Elix, вот текущий набор вещей, которые нам нужно сделать для IE 11:

  • Перенести все в ES5.
  • Объедините все в файлы сценариев старой школы, поскольку IE не поддерживает модули. (Хотя в настоящее время объединение подходит для производственных развертываний во всех браузерах, мы предпочитаем, чтобы наши модульные тесты и демонстрации выполнялись как нативные модули.В этих контекстах производительность не является основным фактором, и мы предпочитаем работать непосредственно с реальным кодом. Мы тестируем в Firefox с включенной поддержкой модулей, хотя производственный Firefox еще не поддерживает модули по умолчанию.)
  • Поддерживайте процесс сборки в целом. В современных браузерах весь Эликс может работать как есть.
  • Загрузить полифилл Shadow DOM.
  • Загрузить полифилл Custom Elements.
  • Загрузить полифилл Promise.
  • Загрузить среду выполнения, позволяющую использовать транспилированные асинхронные функции.
  • Загрузить полифилл Symbol.
  • Загрузить полифилл для Object.assign .
  • Включите множество обходных путей для устранения недостатков или особенностей IE. Знаете ли вы, что объект classList в IE имеет метод переключения, который не поддерживает стандартный второй аргумент? Теперь мы это знаем, и нам пришлось это обойти.

Все, что мы вынуждены добавить к приведенному выше списку, отдаляет нас все дальше и дальше от металла. Когда мы сталкиваемся с ошибкой, очень трудно быть уверенным в том, где она находится. Это наш код? Или где-то в списке выше?

Я пошутил в Твиттере, что заставить современное веб-приложение работать в IE можно так же, как можно играть в Doom на термостате. На самом деле ситуация хуже. Насколько я знаю, современный термостат имеет лучшее оборудование, чем ПК 1993 года, на которых изначально работал Doom. Лучшее сравнение может состоять в том, что запуск современного веб-приложения в IE аналогичен запуску современной игры, такой как Horizon Zero Dawn, на термостате.

Microsoft Edge 16 намного лучше, чем IE, но все же это не пикник.

Хотя Edge поддерживает многие современные веб-технологии, Microsoft еще не приступила к реализации Shadow DOM и пользовательских элементов. И Edge по-прежнему страдает от некоторых из тех же болезненных и вопиющих проблем, что и его предшественник:

  • Инструменты отладки Edge просто ужасны. Отладчик медленный. Он висит. Он падает. Его набор функций слабый, слабый, слабый. Это настолько ненадежно, что в какой-то момент я не мог заставить юнит-тесты Elix проходить в Edge, если не были закрыты инструменты отладки. Открытие инструментов отладки Edge приведет к сбоям модульного тестирования. Как насчет дружественного взаимодействия с разработчиком?
  • Частота выпуска Edge слишком медленная. Сегодня я изолировал и зарегистрировал ошибку flex-бокса Edge. Даже если Microsoft немедленно исправит ошибку, мы можем подождать большую часть года, прежде чем исправление станет широко доступным.
  • Microsoft не предложила собственного убедительного видения Интернета. Интерес к Windows API как к платформе приложений кажется незначительным и становится все меньше. Если это так, вы могли бы подумать, что Microsoft будет вкладывать значительные средства в будущее, ориентированное на Интернет. Но на конференции Edge Developer Conference прошлым летом презентации Microsoft почти полностью были посвящены идеям, представленным Google давным-давно.

Дело не в том, что Microsoft забыла о разработчиках и о том, как их обслуживать. Я постоянно впечатлен скоростью и качеством работы, которая в настоящее время выполняется, например, в Visual Studio Code и TypeScript. Но когда дело доходит до взаимодействия с разработчиками в современных браузерах, Edge на последнем месте.

Доля рынка и инерция

При обсуждении поддержки браузеров часто считается важной поддержка IE и Edge. Но, учитывая текущее состояние рынка и некоторые сведения об использовании, я не уверен, что это имеет смысл.

Использование мобильного браузера превышает использование настольного браузера. На мобильных устройствах китайский браузер UC занимает значительно большую долю рынка, чем IE и Edge на настольных компьютерах. Samsung Internet для Android также, возможно, уже превзошел Edge по доле рынка и вскоре может превзойти IE.

Стоимость поддержания работоспособности в IE неуклонно растет. Даже когда новые достижения в Интернете приходят с полифиллами, работающими в IE, совокупный вес всего, что необходимо для поддержки IE, значителен. Насколько быстрее могла бы работать ваша команда, если бы ей не нужно было поддерживать IE? Я предполагаю, что в случае с проектом Elix поддержка IE поглощает 10 % нашего времени и 50 % наших положительных эмоций.

И хотя Edge является заменой Microsoft для IE, не ясно, находится ли Edge на пути к какой-либо интересной позиции на рынке. За 2 года существования Edge на рынке он добился незначительных успехов. Насколько я могу судить, все, кто может отказаться от IE, уже перешли на Chrome. И эти пользователи Chrome должны продолжать использовать Chrome, даже когда они переходят на компьютер с Windows, на котором можно запустить Edge.

В глобальном политическом порядке страна Франция сохраняет за собой одно из 5 постоянных мест в Совете Безопасности ООН исключительно по историческим причинам, вне всякой связи с ее нынешним значением. Похоже, что аналогичные исторические причины вскоре могут стать основным оправданием позиции Microsoft в списках требований к браузерам веб-приложений. Microsoft действует так, как будто автоматически заслуживает места за столом, но я сомневаюсь в этом.Разумно спросить Microsoft: Что вы делаете сегодня как поставщик браузера, что оправдывает время и энергию, которые вы заставляете разработчиков тратить на вас?

Как человек, много лет проработавший в Microsoft, я не держу на них зла. Наоборот, как выпускник, я действительно хочу, чтобы они были успешными. Но если они собираются быть актуальными как поставщики браузеров, им придется работать намного лучше. А пока мне интересно, стоят ли их браузеры затраченных усилий.

Важным аспектом веб-компонентов является инкапсуляция. Это позволяет сохранить структуру, стиль и поведение разметки скрытыми и отделенными от остального кода на странице, чтобы различные части не конфликтовали, а код оставался красивым и чистым. . API Shadow DOM является ключевой частью этого, предоставляя способ присоединения скрытого отдельного DOM к элементу. В этой статье рассматриваются основы использования Shadow DOM.

Примечание. Shadow DOM по умолчанию поддерживается в Firefox (63 и более поздних версиях), Chrome, Opera и Safari. Новый Edge на основе Chromium (79 и более поздние версии) также поддерживает его; в старом Edge этого не было.

Общий вид

В этой статье предполагается, что вы уже знакомы с концепцией DOM (объектная модель документа) — древовидной структурой связанных узлов, которая представляет различные элементы и строки текста, появляющиеся в документе разметки (обычно HTML-документ в в случае веб-документов). В качестве примера рассмотрим следующий фрагмент HTML:

Shadow DOM позволяет присоединять скрытые деревья DOM к элементам в обычном дереве DOM — это теневое дерево DOM начинается с теневого корня, под которым можно прикрепить любые элементы, которые вы хотите, в так же, как обычный DOM.

Есть некоторые моменты терминологии теневого DOM, о которых следует знать:

  • Теневой хост: обычный узел DOM, к которому подключен теневой DOM.
  • Теневое дерево: дерево DOM внутри теневого DOM.
  • Теневая граница: место, где заканчивается теневой DOM и начинается обычный DOM.
  • Shadow root: корневой узел теневого дерева.

Вы можете воздействовать на узлы в теневой модели DOM точно так же, как и на обычные узлы — например, добавлять дочерние элементы или задавать атрибуты, стилизовать отдельные узлы с помощью element.style.foo или добавлять стиль ко всему дереву теневой модели DOM. внутри элемента. Разница в том, что ни один код внутри теневого DOM не может повлиять ни на что за его пределами, что обеспечивает удобную инкапсуляцию.

Обратите внимание, что теневой DOM ни в коем случае не является чем-то новым — браузеры уже давно используют его для инкапсуляции внутренней структуры элемента. Подумайте, например, об элементе с открытыми элементами управления браузером по умолчанию. Все, что вы видите в DOM, — это элемент, но он содержит ряд кнопок и других элементов управления внутри своего теневого DOM. Спецификация теневого DOM позволила вам фактически управлять теневым DOM ваших собственных пользовательских элементов.

Основное использование

Вы можете прикрепить теневой корень к любому элементу с помощью метода Element.attachShadow(). В качестве параметра принимает объект параметров, который содержит один параметр — режим — со значением open или close :

открытый означает, что вы можете получить доступ к теневой модели DOM с помощью JavaScript, написанного в контексте главной страницы, например, с помощью свойства Element.shadowRoot:

Если вы прикрепите теневой корень к пользовательскому элементу с установленным режимом: закрытый, вы не сможете получить доступ к теневой DOM извне — myCustomElem.shadowRoot возвращает null . Это касается встроенных элементов, содержащих теневые модели DOM, например .

Примечание. Как показано в этом сообщении в блоге, обойти закрытые теневые модели DOM довольно просто, а их полное скрытие зачастую требует больше усилий, чем оно того стоит.

Если вы присоединяете теневой DOM к пользовательскому элементу как часть его конструктора (безусловно, самое полезное применение теневого DOM), вы должны использовать что-то вроде этого:

Когда вы прикрепляете теневой DOM к элементу, для управления им достаточно использовать те же DOM API, что и для обычных манипуляций с DOM:

Простой пример

Теперь давайте рассмотрим простой пример, чтобы продемонстрировать теневой DOM в действии внутри пользовательского элемента —

(см. также живой пример). Это берет значок изображения и текстовую строку и встраивает значок в страницу. Когда значок находится в фокусе, он отображает текст во всплывающем информационном окне для предоставления дополнительной контекстной информации. Начнем с того, что в нашем файле JavaScript мы определяем класс PopUpInfo, который расширяет HTMLElement:

Внутри определения класса мы определяем конструктор элемента, который определяет всю функциональность, которую элемент будет иметь при создании его экземпляра.

Создание теневого корня

Сначала мы присоединяем теневой корень к пользовательскому элементу:

Создание теневой структуры DOM

Стиль теневого DOM

После этого мы создаем элемент и наполняем его CSS, чтобы придать ему стиль:

Присоединение теневого DOM к теневому корню

Последний шаг — присоединение всех созданных элементов к корневому каталогу тени:

Использование нашего пользовательского элемента

После того как класс определен, использовать элемент так же просто, как определить его и поместить на страницу, как описано в разделе Использование пользовательских элементов:

Внутренние и внешние стили

В приведенном выше примере мы применяем стиль к Shadow DOM с помощью элемента, но вполне возможно сделать это, ссылаясь вместо этого на внешнюю таблицу стилей из элемента.

Например, взгляните на этот код из нашего примера popup-info-box-external-stylesheet (см. исходный код):

Многие современные браузеры реализуют оптимизацию тегов, либо клонированных из общего узла, либо тегов с идентичным текстом, чтобы позволить им совместно использовать единую вспомогательную таблицу стилей. При такой оптимизации производительность внешних и внутренних стилей должна быть одинаковой.

Читайте также: