代表的な面接トピック

フロントエンド面接:action.navigate はクリックフォールバックとどのように連携すべきか?

フロントエンド普通
Offer.cc 編集チーム公開日 更新日

質問

注文の表示、配達の確認、ヘルプのアクションを持つ注文通知を設計し、各アクションのナビゲーション優先度、フォールバック処理、および冪等性について説明してください。

1. プロンプトと適用範囲

注文ステータス通知には、「注文を表示」、「配達を確認」、「サポートに問い合わせ」のアクションが用意されています。アプリが閉じられている、ウィンドウがすでに存在する、またはセッションが期限切れになっている可能性があります。Notification アクションを使用してフローを設計し、ブラウザナビゲーションが notificationclick とどのように連携するかを説明してください。

2. 面接官がテストしているポイント

  • 本体の navigate とアクションの navigate は別々の URL であり、アクション URL はカスタム処理より優先されることを理解しているか。
  • URL のないアクションは notificationclick に到達することを知っており、event.waitUntil で非同期処理を維持できるか。
  • 同一オリジンの URL、権限、認証状態、および Service Worker のライフタイムを検証できるか。
  • タグ、ビジネス冪等性キー、クライアントリカバリを使用して、重複した確認やウィンドウの作成を防げるか。

3. 最初に明確にすべき質問

  1. 「配達を確認」は GET ナビゲーションにできますか、それとも確認を伴う POST API でなければなりませんか?
  2. 期限切れのセッションでは、まずログインを開いてからアクションを再開すべきですか?
  3. サポートアクションは外部のカスタマーサービスドメインをターゲットにできますか?
  4. 既存のウィンドウをフォーカスする、メッセージを送信する、または新しいウィンドウを開くべきですか?

4. 30秒の回答

読み取り専用の表示アクションとヘルプアクションは許可リストに登録された同一オリジン URL にマッピングし、副作用を伴う確認アクションには navigate を設定しないでおきます。Service Worker が冪等な API を介してそのアクションを処理し、結果ページにルーティングします。すべての URL を検証し、非同期処理を waitUntil でラップし、フォールバックを開く前に既存の制御されたウィンドウをフォーカスします。

5. ステップバイステップの詳細解説

ステップ 1: 本体およびアクションの URL を宣言する

js
await self.registration.showNotification("Order #123", {
  body: "Choose an action",
  tag: "order-123",
  navigate: "/orders/123",
  data: { orderId: "123", version: 4 },
  actions: [
    { action: "view", title: "View order", navigate: "/orders/123" },
    { action: "confirm", title: "Confirm delivery" },
    { action: "help", title: "Contact support", navigate: "/support/orders/123" },
  ],
});

本体およびアクションの URL は、検証済みの同一オリジンルートである必要があります。tag を使用して 1 つの注文の通知を更新または結合し、data にはリカバリに必要な機密性のない識別子のみを保持します。

ステップ 2: 読み取り専用アクションと副作用を伴うアクションを分離する

表示アクションとヘルプアクションはナビゲーションのみを実行し、ブラウザによって処理される場合があります。確認アクションには navigate がないため、notificationclick に到達します。クエリパラメータで状態変更をエンコードするのではなく、冪等なサーバー API を呼び出す必要があります。

ステップ 3: notificationclick フォールバックを実装する

js
self.addEventListener("notificationclick", (event) => {
  event.notification.close();
  const { orderId, version } = event.notification.data ?? {};
  if (event.action !== "confirm" || !orderId) return;

  event.waitUntil(confirmDelivery(orderId, version).then(() =>
    focusOrOpen(`/orders/${encodeURIComponent(orderId)}?confirmed=1`)));
});

プロダクションコードは、ネットワーク障害、古いバージョン、および未承認レスポンスをキャッチし、結果ページでリカバリを表示できるようにする必要があります。waitUntil は、Promise が確定するまで Service Worker イベントを維持します。

ステップ 4: ウィンドウとログインリカバリを処理する

focusOrOpen は、制御された同一オリジンウィンドウに一致させ、postMessage を介して検証済みルートを送信し、適切なウィンドウが存在しない場合にのみ clients.openWindow を呼び出す必要があります。ログインが期限切れの場合は、短命のターゲット状態のみを保持します。サインイン後、ページは再度注文を取得して認可を受ける必要があります。

ステップ 5: セキュリティ、権限、および冪等性

通知の権限は表示を制御するものであり、注文の認可を制御するものではありません。プロトコル、オリジン、パスを制限し、注文 ID、バージョン、または冪等性キーを使用して重複した確認を防止します。破棄、重複タグ、同時デバイスクリックには、明示的なサーバー状態マシンの動作が必要です。

6. 模範的な高品質の回答

表示とヘルプには同一オリジンの navigate を使用し、「配達を確認」には副作用があるため notificationclick で処理します。Service Worker は waitUntil を使用して注文バージョンとキーを含む冪等 API を呼び出し、既存のウィンドウをフォーカスするか結果ルートを開きます。すべての URL は許可リストに登録され、権限は認可とは別であり、ログインリカバリでアクセスを再確認します。テストでは、本体およびアクションのクリック、リピート、オフラインモード、古いバージョン、および複数のウィンドウをカバーします。

7. よくある間違い

  • GET URL 経由で確認をトリガーする → プリフェッチやリピートによって副作用が発生する → イベントから冪等な POST を呼び出す。
  • URL のないアクションが本体の URL を開くと想定する → 動作が曖昧になる → notificationclick で明示的に処理する。
  • data に完全な注文データを入れる → 機密情報が漏洩する → 識別子のみを保持し、認可されたデータを再取得する。
  • 非同期処理の周囲で waitUntil を省略する → Worker が途中で終了する可能性がある → すべての重要な Promise を管理する。
  • 常に新しいウィンドウを作成する → 状態が分裂する → 最初に同一オリジンウィンドウを照合してフォーカスする。

8. フォローアップの質問

フォローアップ 1: action.navigate と notificationclick のどちらが優先されますか?

アクションに固有の navigate がある場合、ブラウザはその URL を使用できます。それを持たないアクションには、カスタムの notificationclick 処理が必要です。

フォローアップ 2: なぜ確認処理を URL に含めないのですか?

ナビゲーションはプリフェッチ、再送、または繰り返しクリックされる可能性があり、副作用を安全に表現できません。確認処理は、認可された冪等なサーバー状態遷移に属します。

フォローアップ 3: 期限切れのログインからどのように回復しますか?

ログインを開く際に、短命で完全性が保護されたターゲット状態を保存します。サインイン後、ページが注文を取得し、認可をチェックした上で、結果ルートを復元します。

フォローアップ 4: 重複実行がないことをどのように証明しますか?

同じ注文バージョンと冪等性キーを使用して、ウィンドウやデバイス間で本体およびアクションのクリックを繰り返しトリガーし、サーバーが確認遷移を 1 回だけ実行することを検証します。

公開情報ソース

関連する質問