DevTools アーキテクチャの更新: DevTools での CSS インフラストラクチャのモダナイゼーション
この記事は、DevTools のアーキテクチャとその構築方法に対する変更について説明する一連のブログ投稿の一部です。これまでの DevTools での CSS の仕組みと、JavaScript ファイルで CSS を読み込むウェブ標準ソリューションへの移行(最終的に)に備えて、DevTools で CSS をモダナイズした方法について説明します。
DevTools の CSS の以前の状態
DevTools では、CSS を 2 つの異なる方法で実装しています。1 つは DevTools のレガシー部分で使用される CSS ファイル用で、もう 1 つは DevTools で使用されているモダン ウェブ コンポーネント用です。
DevTools の CSS 実装は数年前に定義されたもので、現在は古くなっています。DevTools では module.json
パターンが使用され続けており、これらのファイルを削除するために多大な労力がかかっています。これらのファイルを削除する最後の障害は、CSS ファイルの読み込みに使用される resources
セクションです。
最終的に CSS モジュール スクリプトに変換できるさまざまなソリューションの候補を検討することにしました。目的は、レガシー システムによって生じた技術的負債を解消することと、CSS モジュール スクリプトへの移行プロセスを容易にすることでした。
DevTools に存在する CSS ファイルは、module.json
ファイルを使用して読み込まれるため、「以前の」ファイルと見なされ、削除の対象となっています。すべての CSS ファイルは、CSS ファイルと同じディレクトリにある module.json
ファイルの resources
に登録する必要がありました。
残りの 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 ファイルの内容が取得され、appendStyle
関数を使用して <style>
要素としてページに挿入されます。
インライン スタイル要素を使用して CSS を追加する appendStyle
関数:
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);
モダン ウェブ コンポーネント(カスタム要素を使用)を導入した際、当初はコンポーネント ファイル自体でインライン <style>
タグを使用して CSS を使用することを決定しました。これには独自の課題がありました。
- 構文のハイライト表示がサポートされていない。インライン CSS の構文のハイライト表示を提供するプラグインは、
.css
ファイルで記述された CSS の構文のハイライト表示や自動補完機能ほど優れているわけではありません。 - ビルドのパフォーマンス オーバーヘッド。また、インライン CSS では、lint チェックに 2 つのパス(CSS ファイル用とインライン CSS 用)が必要でした。これは、すべての CSS がスタンドアロンの CSS ファイルに記述されていれば削除できるパフォーマンス オーバーヘッドでした。
- 圧縮に関する課題インライン CSS を簡単に圧縮できなかったため、CSS は圧縮されませんでした。DevTools のリリースビルドのファイルサイズも、同じウェブ コンポーネントの複数のインスタンスによって重複する CSS が導入されたことにより、大きくなりました。
インターンシップ プロジェクトの目標は、DevTools で使われている以前のインフラストラクチャと新しいウェブ コンポーネントの両方で機能する CSS インフラストラクチャのソリューションを見つけることでした。
考えられるソリューションの調査
この問題は 2 つの部分に分けることができます。
- ビルドシステムが CSS ファイルをどう扱うかを理解する
- CSS ファイルが DevTools によってインポートされ、使用される仕組みを把握する。
各要素に対して考えられるさまざまなソリューションを検討しました。以下でそれらの概要を説明します。
CSS ファイルのインポート
TypeScript ファイルに CSS をインポートして使用する目的は、ウェブ標準にできるだけ忠実に従い、DevTools 全体で一貫性を保ち、HTML で CSS の重複を回避することでした。また、変更を新しいウェブ プラットフォーム標準(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
オブジェクトとしてインポートし、adoptedStyleSheets
プロパティを使用して Shadow DOM(DevTools では数年前から Shadow DOM を使用)に追加する方法を探しました。
バンドル オプション
TypeScript ファイルで簡単に操作できるように、CSS ファイルを CSSStyleSheet
オブジェクトに変換する方法が必要でした。Google では、この変換を行うバンドルツールとして、Rollup と webpack の両方を検討しました。DevTools はすでに本番環境ビルドで Rollup を使用していますが、いずれかのバンドルを本番環境ビルドに追加すると、現在のビルドシステムでパフォーマンスの問題が発生する可能性があります。Chromium の GN ビルドシステムとの統合により、バンドルが難しくなるため、バンドラは現在の Chromium ビルドシステムと適切に統合されない傾向があります。
代わりに、現在の GN ビルドシステムを使用してこの変換を行う方法を検討しました。
DevTools で CSS を使用する新しいインフラストラクチャ
新しいソリューションでは、adoptedStyleSheets
を使用して特定の Shadow DOM にスタイルを追加し、GN ビルドシステムを使用して、document
または ShadowRoot
で採用できる CSSStyleSheet オブジェクトを生成します。
// 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 ファイル内のクラス名や ID セレクタの重複による問題を回避できます
- CSS モジュール スクリプトやインポート アサーションなどの将来のウェブ標準への移行が容易
この解決策の唯一の注意点は、import
ステートメントで .css.js
ファイルをインポートする必要があることです。GN がビルド中に CSS ファイルを生成できるように、generate_css_js_files.js
スクリプトを作成しました。ビルドシステムはすべての CSS ファイルを処理して、デフォルトで CSSStyleSheet
オブジェクトをエクスポートする JavaScript ファイルに変換します。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
の以前の使用を移行するプロセスはより複雑でした。以前のスタイルを登録する 2 つの主な関数は registerRequiredCSS
と createShadowRootWithCoreStyles
でした。これらの呼び出しを移行する手順は非常に機械的であるため、ESLint ルールを使用して修正を適用し、レガシー コードを自動的に移行することにしました。DevTools では、DevTools コードベースに固有のカスタムルールがすでに使用されています。これは、ESLint がすでにコードを解析して抽象構文ツリー(AST)に変換しているため、役に立ちました。AST)を取得し、CSS の登録呼び出しだった特定の呼び出しノードをクエリできます。
移行 ESLint ルールの作成時に直面した最大の問題は、エッジケースをキャプチャすることでした。キャプチャする価値のあるエッジケースと手動で移行する必要があるエッジケースのバランスを適切に取ることが重要でした。また、インポートされた .css.js
ファイルがビルドシステムによって自動的に生成されていない場合は、ユーザーに通知できるようにしました。これにより、実行時にファイルが見つからないエラーを防ぐことができます。
移行に ESLint ルールを使用することの欠点の 1 つは、システムで必要な GN ビルドファイルを変更できなかったことです。これらの変更は、各ディレクトリでユーザーが手動で行う必要がありました。これはより多くの作業が必要でしたが、インポートされるすべての .css.js
ファイルが実際にビルドシステムによって生成されていることを確認する良い方法でした。
全体として、この移行に ESLint ルールを使用することで非常に役立ちました。以前のコードを新しいインフラストラクチャに迅速に移行でき、AST をすぐに利用できるようにすることで、ルール内の複数のエッジケースを処理し、ESLint のフィクサ API を使用して確実に自動的に修正できるようになりました。
次のステップ
これまでのところ、Chromium DevTools のすべてのウェブ コンポーネントは、インライン スタイルではなく新しい CSS インフラストラクチャを使用するように移行されています。registerRequiredCSS
の以前の使用のほとんども、新しいシステムを使用するように移行されています。あとは、できるだけ多くの module.json
ファイルを削除し、今後 CSS モジュール スクリプトを実装するために、現在のインフラストラクチャを移行するだけです。
プレビュー チャネルをダウンロードする
デフォルトの開発用ブラウザとして Chrome の Canary、Dev、Beta を使用することを検討してください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたりできます。また、ユーザーよりも早くサイトの問題を見つけることもできます。
Chrome DevTools チームに問い合わせる
次のオプションを使用して、DevTools の新機能、更新、その他のトピックについて話し合います。
- フィードバックや機能リクエストは、crbug.com からお送りください。
- DevTools で [その他] > [ヘルプ] > [DevTools の問題を報告] を使用して、DevTools の問題を報告します。
- @ChromeDevTools でツイートしてください。
- DevTools の新機能に関する YouTube 動画または DevTools のヒントに関する YouTube 動画にコメントを残してください。