Веб-компоненты не установлены на край
Обновлено: 21.11.2024
Веб-компоненты – это набор различных технологий, позволяющих создавать повторно используемые настраиваемые элементы, функциональность которых изолирована от остального кода, и использовать их в своих веб-приложениях.
Концепции и использование
Как разработчики, мы все знаем, что повторное использование кода — это хорошая идея. Традиционно это было не так просто для пользовательских структур разметки — подумайте о сложном HTML (и связанном с ним стиле и сценарии), который вам иногда приходилось писать для отображения пользовательских элементов управления пользовательского интерфейса, и о том, как их многократное использование может превратить вашу страницу в беспорядок. если вы не будете осторожны.
Веб-компоненты предназначены для решения таких проблем — они состоят из трех основных технологий, которые можно использовать вместе для создания универсальных пользовательских элементов с инкапсулированной функциональностью, которые можно повторно использовать где угодно, не опасаясь коллизий кода.
- Пользовательские элементы. Набор API-интерфейсов JavaScript, которые позволяют определять настраиваемые элементы и их поведение, которые затем можно использовать по желанию в пользовательском интерфейсе.
- Shadow DOM: набор API-интерфейсов JavaScript для присоединения инкапсулированного "теневого" дерева DOM к элементу, который отображается отдельно от основного документа DOM, и управления соответствующей функциональностью. Таким образом, вы можете сохранить функции элемента в тайне, чтобы их можно было записывать в сценарии и стилизовать, не опасаясь столкновения с другими частями документа.
- Шаблоны HTML. Элементы и позволяют создавать шаблоны разметки, которые не отображаются на отображаемой странице. Затем их можно многократно использовать в качестве основы структуры пользовательского элемента.
Основной подход к реализации веб-компонента обычно выглядит примерно так:
- Создайте класс, в котором вы указываете функциональность веб-компонента, используя синтаксис класса ECMAScript 2015 (дополнительную информацию см. в разделе Классы).
- Зарегистрируйте новый настраиваемый элемент с помощью метода CustomElementRegistry.define(), передав ему имя определяемого элемента, класс или функцию, в которой указаны его функциональные возможности, и, при необходимости, элемент, от которого он наследуется.
- При необходимости присоедините теневой DOM к пользовательскому элементу с помощью метода Element.attachShadow(). Добавьте дочерние элементы, прослушиватели событий и т. д. в теневой DOM с помощью обычных методов DOM.
- При необходимости определите шаблон HTML с помощью и . Снова используйте обычные методы DOM, чтобы клонировать шаблон и прикрепить его к теневой модели DOM.
- Используйте свой пользовательский элемент в любом месте страницы, как и любой обычный элемент 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 (см. исходный код):
Многие современные браузеры реализуют оптимизацию тегов, либо клонированных из общего узла, либо тегов с идентичным текстом, чтобы позволить им совместно использовать единую вспомогательную таблицу стилей. При такой оптимизации производительность внешних и внутренних стилей должна быть одинаковой.
Читайте также:
- Карта сообщений для блокнота, показывающая 0
- Что произойдет, если коэффициент qr равен коэффициенту быстрой ликвидности, равному 0 6
- Набор микросхем, предназначенных для временного хранения данных при включении компьютера
- Что из следующего относится к памяти Samsung Galaxy Z Flip3
- Денис Райдер, между нами никого не будет