Модернизация инфраструктуры CSS в DevTools

Обновление архитектуры DevTools: модернизация инфраструктуры CSS в DevTools

Этот пост является частью серии постов в блоге, описывающих изменения, которые мы вносим в архитектуру DevTools и то, как она построена. Мы объясним, как исторически CSS работал в DevTools и как мы модернизировали наш CSS в DevTools в рамках подготовки (в конечном итоге) к переходу на веб-стандартное решение для загрузки CSS в файлы JavaScript.

Предыдущее состояние CSS в DevTools

DevTools реализовал CSS двумя разными способами: один для файлов CSS, используемых в устаревшей части DevTools, другой для современных веб-компонентов , которые используются в DevTools.

Реализация CSS в DevTools была определена много лет назад и сейчас устарела. DevTools придерживается шаблона module.json , и для удаления этих файлов было приложено немало усилий. Последним блокировщиком удаления этих файлов является раздел resources , который используется для загрузки файлов CSS.

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

Любые файлы CSS, которые были в DevTools, считались «устаревшими», поскольку они были загружены с использованием файла module.json , который находится в процессе удаления. Все файлы CSS должны были быть перечислены в resources в файле module.json в том же каталоге, что и файл CSS.

Пример оставшегося файла module.json :

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

Эти файлы CSS затем будут заполнять глобальную карту объектов под названием Root.Runtime.cachedResources как отображение пути к их содержимому. Чтобы добавить стили в DevTools, вам нужно будет вызвать registerRequiredCSS , указав точный путь к файлу, который вы хотите загрузить.

Пример вызова registerRequiredCSS :

constructor() {
  …
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  …
}

Это позволит получить содержимое файла CSS и вставить его как элемент <style> на страницу с помощью функции appendStyle :.

Функция appendStyle , которая добавляет CSS с помощью встроенного элемента стиля:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

Когда мы представили современные веб-компоненты (с использованием пользовательских элементов), мы решили сначала использовать CSS через встроенные теги <style> в самих файлах компонентов . Это создало свои проблемы:

  • Отсутствие поддержки подсветки синтаксиса. Плагины, обеспечивающие подсветку синтаксиса для встроенного CSS, как правило, не так хороши, как функции подсветки синтаксиса и автозаполнения для CSS, написанные в файлах .css .
  • Увеличивайте производительность. Встроенный CSS также означал, что для проверки необходимо было выполнить два прохода: один для файлов CSS и один для встроенного CSS. Это накладные расходы на производительность, которые мы могли бы устранить, если бы весь CSS был написан в отдельных файлах CSS.
  • Задача по минификации. Встроенный CSS нелегко минимизировать, поэтому ни один CSS не был минимизирован. Размер файла релизной сборки DevTools также был увеличен из-за дублированного CSS, введенного несколькими экземплярами одного и того же веб-компонента.

Целью моего проекта стажировки было найти решение для инфраструктуры CSS, которое работало бы как с устаревшей инфраструктурой, так и с новыми веб-компонентами, используемыми в DevTools.

Исследование потенциальных решений

Проблему можно разделить на две разные части:

  • Выяснение того, как система сборки работает с файлами CSS.
  • Выяснение того, как файлы CSS импортируются и используются DevTools.

Мы рассмотрели различные потенциальные решения для каждой части, они описаны ниже.

Импорт файлов CSS

Цель импорта и использования CSS в файлах TypeScript заключалась в том, чтобы максимально приблизиться к веб-стандартам, обеспечить согласованность во всех инструментах DevTools и избежать дублирования CSS в нашем HTML. Мы также хотели иметь возможность выбрать решение, которое позволило бы перенести наши изменения на новые стандарты веб-платформы, такие как сценарии модулей CSS.

По этим причинам операторы @import и Теги не показались мне подходящими для DevTools. Они не будут одинаковыми с импортом в остальных DevTools и приведут к Flash Of Unstyled Content (FOUC) . Переход на сценарии модулей CSS будет сложнее, поскольку импорт придется явно добавлять и обрабатывать иначе, чем с тегами <link> .

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

Возможные решения с использованием @import или <link> .

Вместо этого мы решили найти способ импортировать файл CSS как объект CSSStyleSheet , чтобы можно было добавить его в Shadow Dom (DevTools использует Shadow DOM уже пару лет), используя его adoptedStyleSheets свойствоStyleSheets.

Параметры бандлера

Нам нужен был способ преобразования файлов CSS в объект CSSStyleSheet , чтобы мы могли легко манипулировать им в файле TypeScript. Мы рассматривали как Rollup , так и Webpack в качестве потенциальных сборщиков, которые сделают это преобразование за нас. DevTools уже использует Rollup в своей производственной сборке, но добавление любого упаковщика в производственную сборку может привести к потенциальным проблемам с производительностью при работе с нашей текущей системой сборки. Наша интеграция с системой сборки Chromium GN усложняет объединение, и поэтому сборщики, как правило, плохо интегрируются с текущей системой сборки Chromium.

Вместо этого мы изучили возможность использовать текущую систему сборки GN, чтобы выполнить это преобразование за нас.

Новая инфраструктура использования CSS в DevTools

Новое решение предполагает использование adoptedStyleSheets для добавления стилей в конкретный Shadow DOM, а также использование системы сборки GN для создания объектов CSSStyleSheet, которые могут быть приняты document или ShadowRoot .

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  …
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

Использование adoptedStyleSheets имеет множество преимуществ, в том числе:

  • Он постепенно становится современным веб-стандартом.
  • Предотвращает дублирование CSS
  • Применяет стили только к Shadow DOM, что позволяет избежать любых проблем, вызванных повторяющимися именами классов или селекторами идентификаторов в файлах CSS.
  • Легко перейти на будущие веб-стандарты, такие как сценарии модулей CSS и утверждения импорта.

Единственное предостережение в этом решении заключалось в том, что операторы import требовали импорта файла .css.js . Чтобы GN мог генерировать CSS-файл во время сборки, мы написали generate_css_js_files.js . Система сборки теперь обрабатывает каждый файл CSS и преобразует его в файл JavaScript, который по умолчанию экспортирует объект CSSStyleSheet . Это здорово, поскольку мы можем импортировать файл CSS и легко его адаптировать. Кроме того, теперь мы можем легко минимизировать производственную сборку, сохраняя размер файла:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

Пример создания iconButton.css.js из скрипта.

Миграция устаревшего кода с использованием правил ESLint

Хотя веб-компоненты можно было легко перенести вручную, процесс миграции устаревших вариантов использования registerRequiredCSS был более сложным. Двумя основными функциями, которые регистрировали устаревшие стили, были registerRequiredCSS и createShadowRootWithCoreStyles . Мы решили, что, поскольку шаги по переносу этих вызовов являются достаточно механическими, мы можем использовать правила ESLint для применения исправлений и автоматического переноса устаревшего кода. DevTools уже использует ряд пользовательских правил, специфичных для кодовой базы DevTools. Это было полезно, поскольку ESLint уже анализировал код в абстрактное синтаксическое дерево (сокр. AST), и мы могли запрашивать конкретные узлы вызовов, которые были вызовами для регистрации CSS.

Самой большой проблемой, с которой мы столкнулись при написании правил миграции ESLint, был учет крайних случаев. Мы хотели убедиться, что нам удалось найти правильный баланс между знанием того, какие крайние случаи стоит зафиксировать, а какие следует перенести вручную. Мы также хотели иметь возможность сообщать пользователю, что импортированный файл .css.js не создается автоматически системой сборки, поскольку это предотвращает возникновение ошибок «файл не найден» во время выполнения.

Одним из недостатков использования правил ESLint для миграции было то, что мы не могли изменить необходимый файл сборки GN в системе. Эти изменения должны были быть сделаны пользователем вручную в каждом каталоге. Хотя это потребовало дополнительной работы, это был хороший способ убедиться, что каждый импортируемый файл .css.js действительно создается системой сборки.

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

Что дальше?

На данный момент все веб-компоненты в Chromium DevTools были перенесены для использования новой инфраструктуры CSS вместо использования встроенных стилей. Большинство устаревших вариантов использования registerRequiredCSS также были перенесены для использования в новой системе. Все, что осталось, — это удалить как можно больше файлов module.json , а затем перенести эту текущую инфраструктуру для реализации сценариев модулей CSS в будущем!

Загрузите предварительный просмотр каналов

Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти каналы предварительного просмотра дают вам доступ к новейшим функциям DevTools, тестируют передовые API-интерфейсы веб-платформы и находят проблемы на вашем сайте раньше, чем это сделают ваши пользователи!

Связь с командой Chrome DevTools

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