代表的な面接トピック

フロントエンド面接:プログレッシブハイドレーションを備えた宣言的 Shadow DOM の設計

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

サーバーレンダリングされた製品カードは、JavaScript が読み込まれる前に機能し、スタイルをカプセル化し、ハイドレーション後にインタラクティブになる必要があります。宣言的 Shadow DOM によるアプローチを設計し、open と closed のルート、スロット投影、フォールバックマークアップ、カスタム要素のアップグレード、ストリーミング、およびテストについて説明してください。

プロンプトとスコープ

ページはサーバーから製品カードをストリーミングします。各カードは JavaScript なしで意味のあるコンテンツをレンダリングし、コンポーネントのスタイルを分離したままにし、クライアントバンドルが到着したときに動作をアタッチする必要があります。宣言的シャドウルートを使用し、属性をパースしないブラウザや HTML が存在した後にアップグレードするカスタム要素に対するプログレッシブエンハンスメントについて説明してください。

コアスキルはブラウザのレンダリング、コンポーネントの境界、およびハイドレーションの正確性であるため、これは frontend に属します。

面接官が評価するポイント

第一に、shadowrootmode="open" または "closed" を持つテンプレートが、サポートされている場合に HTML パーサーによってシャドウルートに変換されることを知っているか。

第二に、ホストに対してアタッチされるのは最初の宣言的シャドウルートのみであり、後続のものはテンプレートのまま残り、意図的なフォールバック戦略が必要であることを説明できるか。

第三に、サーバー HTML とクライアントのアップグレード全体で、スロット、スタイル、アクセシビリティ、およびフォームの動作を維持できるか。

第四に、open による検査と closed によるカプセル化を区別できるか。closed モードは shadowRoot の参照を隠すだけであり、セキュリティ境界ではありません。

第五に、ハイドレーションやストリーミング中の二重レンダリング、重複リスナー、イベントの損失を防ぐことができるか。

最初に明確にすべき質問

  • どのブラウザとサーバーレンダリング応答モードがサポートされていますか?
  • ホストはすぐにカスタム要素になりますか、それとも後からアップグレードできますか?
  • どの小要素がスロットに配置され、利用者は ::slotted でスタイルを設定する必要がありますか?
  • テストや統合のために open モードが必要ですか、それとも closed モードが製品の制約ですか?
  • ページはネストされたコンポーネントをストリーミングしますか、それとも1つのレスポンスで完全なルートを送信しますか?
  • 宣言的パースが利用できない場合のフォールバックは何ですか?

30秒の回答フレームワーク

「私は、宣言的シャドウルートとセマンティックなライト DOM フォールバックを使用してコンポーネントをサーバーレンダリングし、すでにパースされたルートを再作成することなくカスタム要素のアップグレードで動作をアタッチします。パブリックコンテンツにはスロットを使用し、スタイルをルート内に保持し、セキュリティ制御ではなく統合の決定として open または closed モードを選択し、shadowRootMode の機能検出を行います。ハイドレーションは冪等であり、イベントは委任されるか1回だけバインドされ、テストでは非対応ブラウザ、ストリーミング順序、スロット、アクセシビリティ、アップグレードのタイミングをカバーします。」

ステップバイステップの回答

ステップ 1: セマンティックルートのレンダリング

サーバーは宣言的テンプレートを持つホストを出力します。JavaScript の実行前でもレスポンスが有用であるように、テキストとコントロールを意味のあるものに保ちます。ツールや統合で検査が必要な場合は open を使用し、コンポーネントの規約が意図的に参照を隠す場合にのみ closed を使用します。

html
<product-card>
  <template shadowrootmode="open">
    <style>:host { display: block }</style>
    <article><slot name="title"></slot><button>Buy</button></article>
  </template>
  <span slot="title">Keyboard</span>
</product-card>

スロットの割り当てはパブリックな規約の一部です。アクセシビリティのセマンティクスも制御している場合を除き、シャドウツリーとフォールバックコンテンツの両方でタイトルを重複させないでください。

ステップ 2: アップグレード規約の定義

カスタム要素クラスが定義されている場合、そのライフサイクルは attachShadow を再度呼び出すのではなく、既存のシャドウルートを検出する必要があります。状態の初期化とリスナーのバインドを1回だけ行い、サーバーから提供されたノードをそのまま残します。ブラウザがルートを作成しなかった場合、要素はテンプレートからルートを作成するか、互換性のあるライト DOM フォールバックをレンダリングできます。

ステップ 3: 機能検出とフォールバック

宣言的な動作に依存する前に、小さなパーサープローブまたは関連するテンプレートプロパティでサポートを確認します。非対応のブラウザでもセマンティックなライト DOM コンテンツを受け取る必要があります。クライアント側のアップグレードで後からシャドウルートをアタッチすることもできますが、遷移中にコンテンツを非表示にしてはなりません。

ステップ 4: スロットとスタイルの維持

スロットはライト DOM の子要素をシャドウツリーに投影します。名前付きスロット、デフォルトスロットの動作、および ::slotted などのスタイリング制限をドキュメント化します。コンポーネントのスタイルをルート内に保持し、境界を越えられないセレクタに依存する代わりに、意図的なカスタムプロパティまたは parts を公開します。

ステップ 5: ストリーミングとネストの処理

ストリーミングでは、ネストされたスロット付きの子要素の前や、カスタム要素の定義の前にホストが配信されることがあります。パース順序を想定された状態として扱います。子要素が到着したときにスロットが更新される必要があり、ストリームの完了前でも完了後でもアップグレードが安全である必要があります。ホストを2つ目のツリーで置き換えることは避けてください。

ステップ 6: 重複作業なしでの動作のハイドレーション

イベントハンドラーは1回だけアタッチし、繰り返されるカードの場合はルートレベルのデリゲートを使用することが望ましいです。利用者が上書きできるパブリック属性ではなく、プライベートフィールドまたは WeakMap で初期化をマークします。動作がアタッチされたときにフォームの状態とフォーカスを保持します。

ステップ 7: ブラウザとアクセシビリティ境界のテスト

サポート対象および非サポート対象のパーサー、open モードと closed モード、単一対複数の宣言的ルート、遅れて到着するスロット、パース前後のカスタム要素アップグレード、キーボードフォーカス、ラベル、フォーム送信、ネットワーク中断後のハイドレーションをテストします。インタラクティブなコントロールツリーが1つだけ存在し、重複したイベントがないことを検証します。

模範解答

「私は、セマンティックなホストと1つの宣言的シャドウルートをストリーミングし、パブリックコンテンツに名前付きスロットを使用し、スタイルをルート内に保持します。カスタム要素のアップグレードでは、まずルートがすでに存在するかどうかを確認するため、ハイドレーションはサーバーマークアップを置き換えるのではなく強化します。open モードは検査をサポートし、closed モードは参照を隠すだけでセキュリティ境界ではありません。

パーサーのサポートを機能検出し、ライト DOM のフォールバックコンテンツを保持します。ストリーミングや遅延アップグレードは通常の状態であるため、スロットの投影と初期化は冪等でなければなりません。テストでは、非対応ブラウザ、ネストされたルート、遅延スロット、フォーカス、フォーム、アクセシビリティ、および重複イベントの防止をカバーします。」

よくある間違い

  • アップグレードのたびに attachShadow を呼び出す → 既存のルートや状態が失われる → パースされたルートを再利用する。
  • closed モードをセキュリティとして扱う → 呼び出し元は公開された動作を通じて依然として対話可能 → カプセル化としてのみドキュメント化する。
  • フォールバックとシャドウコンテンツを重複させる → スクリーンリーダーが2回読み上げる可能性がある → アクセシブルなソースを1つ定義する。
  • パース完了前にスロットが到着すると想定する → ストリーミングされた子要素がメンタルモデルから消える → 遅延投影をテストする。
  • 内部セレクタでスロット付きコンテンツのスタイルを設定する → ルールが境界を越えない → ::slotted、parts、またはカスタムプロパティを使用する。
  • レンダリングのたびにリスナーをバインドする → クリックが複数回発火する → ハイドレーションを冪等にする。
  • ライト DOM フォールバックを破棄する → 非対応ブラウザで空白のカードが表示される → セマンティックなサーバーマークアップを維持する。

フォローアップの質問

フォローアップ 1: 宣言的シャドウルートは常にアタッチされますか?

いいえ。パーサーのサポートと有効なモードに依存します。非対応のブラウザではテンプレートが通常のコンテンツとして残るため、フォールバックまたはクライアントでのアップグレードが必要です。

フォローアップ 2: ホストあたり1つのルートのみである理由は?

パーサーはそのホストに対する最初の宣言的ルートをアタッチします。競合するルートを作成するのではなく、後続のテンプレートは意図的な処理のために利用可能なまま残ります。

フォローアップ 3: open モードはどのような場合に選択しますか?

テスト、統合、または制御された拡張でルート参照が必要な場合に open モードを選択します。セキュリティ権限を付与または削除するものではありません。

フォローアップ 4: ストリーミング中にスロットはどのように機能しますか?

ホストはスロット付きの子要素よりも前に存在できます。子要素が到着すると、スロットの割り当てが更新されます。テストでは両方の到着順序をカバーする必要があります。

フォローアップ 5: ハイドレーションの不一致(mismatch)を回避するには?

サーバーとクライアントの規約を安定させ、既存のノードを再利用し、2つ目のツリーをレンダリングするのではなく初期化を冪等にします。

フォローアップ 6: closed ルートはどのようにテストしますか?

パブリックな規約を通じて、ユーザーに見える動作、フォーカス、イベント、およびアクセシビリティをテストします。shadowRoot を直接読み取ることに依存しないでください。

公開情報ソース

関連する質問